using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Windows.Forms; namespace DevComponents.DotNetBar.Charts { /// /// DataBinding helper class /// public class DataBinder : IDisposable { #region Private variables private readonly ChartControl _ChartControl; private readonly BaseChart _Chart; private readonly BaseSeries _Series; private CurrencyManager _CurrencyManager; private PropertyDescriptorCollection _Pdc; private DataView _DataView; private BindingSource _BindingSource; private BindingSource _BaseBindingSource; private object _BaseDataSource; private string _BaseDataMember; private bool _NotificationAutogen; private bool _NotificationInProgress; private int _SeriesCount; #endregion /// /// DataBinding /// /// internal DataBinder(BaseChart chart) { _Chart = chart; _ChartControl = chart.ChartControl; } internal DataBinder(BaseSeries series) { _Series = series; _Chart = series.Parent as BaseChart; _ChartControl = _Chart.ChartControl; } #region Internal properties #region BaseDataMember internal string BaseDataMember { get { return (_BaseDataMember); } set { _BaseDataMember = value; } } #endregion #region BaseDataSource internal object BaseDataSource { get { return (_BaseDataSource); } set { _BaseDataSource = value; } } #endregion #region CurrencyManager internal CurrencyManager CurrencyManager { get { return (_CurrencyManager); } set { if (_CurrencyManager != value) { if (_CurrencyManager != null) _CurrencyManager.ListChanged -= CurrencyManagerListChanged; _CurrencyManager = value; _BaseBindingSource = null; if (_CurrencyManager != null) { _Pdc = CurrencyManager.GetItemProperties(); _BaseBindingSource = _BaseDataSource as BindingSource; _CurrencyManager.ListChanged += CurrencyManagerListChanged; } else { _Pdc = null; } } } } #endregion #region DataView internal DataView DataView { get { return (_DataView); } set { _DataView = value; } } #endregion #region GetValue internal object GetValue(int index, string name) { if (CurrencyManager != null && _Pdc != null) { if ((uint)index < CurrencyManager.List.Count) { object dataItem = CurrencyManager.List[index]; PropertyDescriptorCollection pdc = GetPdc(dataItem); PropertyDescriptor pd = FindPropertyDescriptor(pdc, name); if (pd != null) return (pd.GetValue(dataItem)); } } return (null); } #endregion #region Pdc internal PropertyDescriptorCollection Pdc { get { return (_Pdc); } set { _Pdc = value; } } #endregion #region RowCount internal int RowCount { get { if (CurrencyManager != null) return (CurrencyManager.Count); return (0); } } #endregion #endregion #region Clear internal void Clear() { CurrencyManager = null; } #endregion #region DataConnect internal object DataConnect(object dataSource, string dataMember) { _BaseDataSource = dataSource; _BaseDataMember = dataMember; if (_BaseDataSource != null) { _BindingSource = _BaseDataSource as BindingSource; if (_BindingSource != null) { BindingSource bs = _BindingSource.DataSource as BindingSource; if (bs != null) { if (string.IsNullOrEmpty(_BindingSource.DataMember) == false) _BaseDataMember = _BindingSource.DataMember; else _BindingSource = null; while (bs.DataSource is BindingSource) bs = (BindingSource)bs.DataSource; DataSet dss = bs.DataSource as DataSet; if (dss != null) { DataRelation dsr = dss.Relations[_BaseDataMember]; if (dsr != null) { _BaseDataSource = dss; _BaseDataMember = dsr.ChildTable.TableName; } } } else { _BaseDataSource = _BindingSource.DataSource; _BaseDataMember = _BindingSource.DataMember; } } DataView = null; if (_BaseDataSource is DataSet) return (AddDataSetTable((DataSet)_BaseDataSource, _BaseDataMember)); if (_BaseDataSource is DataTable) return (AddDataTable((DataTable)_BaseDataSource)); if (_BaseDataSource is IListSource) return (AddList(((IListSource)_BaseDataSource).GetList())); return (AddList(_BaseDataSource)); } return (null); } #endregion #region AddList internal object AddList(object source) { bool autogen = _Chart.AutoGenSeriesCollection; if (_ChartControl.DoDataBindingStartEvent(_Chart, source, ref autogen) == false) return (AddList(source, autogen)); return (null); } internal object AddList(object source, bool autogen) { if (_Series == null) { if (SetDataConnection(source, autogen) == true) ProcessAddList(source, autogen); } else { SetDataConnection(source, false); } return (source); } #region ProcessAddList private void ProcessAddList(object source, bool autogen) { if (_Pdc != null) { if (autogen == true && _ChartControl.DesignerHosted == false) AutoGenerateListSeries(source); foreach (ChartSeries series in _Chart.BaseSeries) series.NeedToUpdateBindings = true; } } #region AutoGenerateListSeries private void AutoGenerateListSeries(object source) { string nameSeries = _Chart.DataPropertyNameSeries; string nameX = GetListNameX(nameSeries); CustomCollection namesY = GetListNamesY(nameSeries, nameX); if (string.IsNullOrEmpty(nameSeries) == true) AutoGenListSeriesSingle(nameX, namesY); else AutoGenListSeriesMultiple(source, nameSeries, nameX, namesY); } #region AutoGenListSeriesSingle private void AutoGenListSeriesSingle( string nameX, CustomCollection namesY) { BaseSeries series = _Chart.GetNewSeries(); series.DataPropertyNameX = nameX; series.DataPropertyNamesY = namesY; SetAutoSeriesId(series); _Chart.AddChartSeries(series); } #endregion #region AutoGenListSeriesMultiple private void AutoGenListSeriesMultiple(object source, string nameSeries, string nameX, CustomCollection namesY) { PropertyDescriptor pdSeries = FindListPd(nameSeries); if (pdSeries == null) throw new Exception("List series name not found (" + nameSeries + ")"); object lastSeriesKey = null; IList list = source as IList; if (list != null) { for (int i = 0; i < list.Count; i++) { object o = list[i]; if (o != null) { object seriesKey = pdSeries.GetValue(o); if ((lastSeriesKey != null) && (seriesKey != lastSeriesKey)) AddAutoGenSeries(lastSeriesKey, nameSeries, nameX, namesY); lastSeriesKey = seriesKey; } } if (lastSeriesKey != null) AddAutoGenSeries(lastSeriesKey, nameSeries, nameX, namesY); } } #endregion #region GetListNameX private string GetListNameX(string nameSeries) { string nameX = _Chart.DataPropertyNameX; if (string.IsNullOrEmpty(nameX) == false) { if (FindListPd(nameX) == null) throw new Exception("Cannot find List Property (" + nameX + ")"); return (nameX); } bool emptyNameSeries = string.IsNullOrEmpty(nameSeries); foreach (PropertyDescriptor pd in _Pdc) { if (IsAccessibleItem(pd) == true) { if (emptyNameSeries == true || emptyNameSeries.Equals(pd.Name) == false) return (pd.Name); } } throw new Exception("Cannot determine auto-assigned DataPropertyNameX."); } #endregion #region GetListNamesY private CustomCollection GetListNamesY(string nameSeries, string nameX) { CustomCollection namesY = _Chart.DataPropertyNamesY; if (namesY == null) namesY = new CustomCollection(); if (namesY.Count == 0) { int countY = _Chart.GetAutoGenSeriesNameCount(); foreach (PropertyDescriptor pd in _Pdc) { if (IsAccessibleItem(pd) == true) { if ((nameSeries == null || nameSeries.Equals(pd.Name) == false) && nameX.Equals(pd.Name) == false) { namesY.Add(pd.Name); if (--countY == 0) return (namesY); } } } throw new Exception("Cannot determine auto-assigned DataPropertyNamesY."); } return (namesY); } #endregion #region FindListPd private PropertyDescriptor FindListPd(string nameX) { foreach (PropertyDescriptor pd in _Pdc) { if (IsAccessibleItem(pd) == true) { if (pd.Name.Equals(nameX) == true) return (pd); } } return (null); } #endregion #region IsAccessibleItem private bool IsAccessibleItem(PropertyDescriptor pd) { if (pd.PropertyType.IsInterface == false) { if (IsNestedList(pd.PropertyType.Name) == false && IsVisibleItem(pd) == true) return (true); } return (false); } #region IsNestedList private bool IsNestedList(string name) { return (name.Equals("List`1") || name.Equals("BindingList`1")); } #endregion #region IsVisibleItem private bool IsVisibleItem(PropertyDescriptor pd) { foreach (Attribute attr in pd.Attributes) { IsVisibleToChartControl dattr = attr as IsVisibleToChartControl; if (dattr != null) return (dattr.Visible); } return (true); } #endregion #endregion #endregion #endregion #endregion #region AddDataSetTable internal object AddDataSetTable(DataSet dataSet, string dataMember) { if (string.IsNullOrEmpty(dataMember) && dataSet.Tables.Count > 0) dataMember = dataSet.Tables[0].TableName; if (string.IsNullOrEmpty(dataMember) == false) return (AddDataTable(dataSet.Tables[dataMember])); return (null); } #endregion #region AddDataTable internal object AddDataTable(DataTable dt) { if (_Series == null) { bool autogen = _Chart.AutoGenSeriesCollection; if (_ChartControl.DoDataBindingStartEvent(_Chart, dt, ref autogen) == false) { if (SetDataConnection(dt, autogen) == true) { if (CurrencyManager != null) ProcessAddDataTable(dt, autogen); } } } else { SetDataConnection(dt, false); } return (dt); } #region ProcessAddDataTable private void ProcessAddDataTable(DataTable dt, bool autogen) { if (autogen == true && _ChartControl.DesignerHosted == false) AutoGenerateDataTableSeries(dt); dt.DefaultView.RowFilter = ""; DataView = dt.DefaultView; ChartBaseSeriesCollection baseSeries = _Chart.BaseSeries; foreach (BaseSeries series in baseSeries) series.NeedToUpdateBindings = true; } #region AutoGenerateDataTableSeries private void AutoGenerateDataTableSeries(DataTable dt) { string nameSeries = _Chart.DataPropertyNameSeries; string nameX = GetDataTableNameX(dt, nameSeries); CustomCollection namesY = GetDataTableNamesY(dt, nameSeries, nameX); if (string.IsNullOrEmpty(nameSeries) == true) AutoGenDataTableSeriesSingle(dt, nameX, namesY); else AutoGenDataTableSeriesMultiple(dt, nameSeries, nameX, namesY); } #region AutoGenDataTableSeriesSingle private void AutoGenDataTableSeriesSingle( DataTable dt, string nameX, CustomCollection namesY) { BaseSeries series = _Chart.GetNewSeries(); series.DataPropertyNameX = nameX; series.DataPropertyNamesY = namesY; SetAutoSeriesId(series); _Chart.AddChartSeries(series); } #endregion #region AutoGenDataTableSeriesMultiple private void AutoGenDataTableSeriesMultiple(DataTable dt, string nameSeries, string nameX, CustomCollection namesY) { DataColumn dtSeries = FindDataColumn(dt, nameSeries); if (dtSeries == null) throw new Exception("DataTable series name not found (" + nameSeries + ")"); object lastSeriesKey = null; for (int i = 0; i < CurrencyManager.Count; i++) { DataRowView view = CurrencyManager.List[i] as DataRowView; if (view != null) { object seriesKey = view.Row.ItemArray[dtSeries.Ordinal]; if ((lastSeriesKey != null) && (seriesKey != lastSeriesKey)) AddAutoGenSeries(lastSeriesKey, nameSeries, nameX, namesY); lastSeriesKey = seriesKey; } } if (lastSeriesKey != null) AddAutoGenSeries(lastSeriesKey, nameSeries, nameX, namesY); } #region AddAutoGenSeries private void AddAutoGenSeries(object seriesKey, string nameSeries, string nameX, CustomCollection namesY) { string name = seriesKey.ToString(); if (FindChartSeries(name) == null) { BaseSeries series = _Chart.GetNewSeries(); series.SeriesId = ++_SeriesCount; series.Name = name; series.SeriesKey = seriesKey; series.DataPropertyNameSeries = nameSeries; series.DataPropertyNameX = nameX; series.DataPropertyNamesY = namesY; _Chart.AddChartSeries(series); } } #endregion #endregion #region GetDataTableNameX private string GetDataTableNameX(DataTable dt, string nameSeries) { string nameX = _Chart.DataPropertyNameX; if (string.IsNullOrEmpty(nameX) == false) { if (FindDataColumn(dt, nameX) == null) throw new Exception("Cannot find DataColumn (" + nameX + ")"); return (nameX); } bool emptyNameSeries = string.IsNullOrEmpty(nameSeries); foreach (DataColumn dtColumn in dt.Columns) { if ((dtColumn.ColumnMapping & MappingType.Hidden) != MappingType.Hidden) { if (emptyNameSeries == true || emptyNameSeries.Equals(dtColumn.ColumnName) == false) return (dtColumn.ColumnName); } } throw new Exception("Cannot determine auto-assigned DataPropertyNameX."); } #endregion #region GetDataTableNamesY private CustomCollection GetDataTableNamesY(DataTable dt, string nameSeries, string nameX) { CustomCollection namesY = _Chart.DataPropertyNamesY; if (namesY == null) namesY = new CustomCollection(); if (namesY.Count == 0) { int countY = _Chart.GetAutoGenSeriesNameCount(); foreach (DataColumn dtColumn in dt.Columns) { if ((dtColumn.ColumnMapping & MappingType.Hidden) != MappingType.Hidden) { if ((nameSeries == null || nameSeries.Equals(dtColumn.ColumnName) == false) && nameX.Equals(dtColumn.ColumnName) == false) { namesY.Add(dtColumn.ColumnName); if (--countY == 0) return (namesY); } } } throw new Exception("Cannot determine auto-assigned DataPropertyNamesY."); } return (namesY); } #endregion #endregion #region FindDataColumn private DataColumn FindDataColumn(DataTable dt, string name) { if (string.IsNullOrEmpty(name) == false) { foreach (DataColumn dtColumn in dt.Columns) { if ((dtColumn.ColumnMapping & MappingType.Hidden) != MappingType.Hidden) { if (dtColumn.ColumnName.Equals(name) == true) return (dtColumn); } } } return (null); } #endregion #endregion #endregion #region SetAutoSeriesId private void SetAutoSeriesId(BaseSeries series) { series.SeriesId = ++_SeriesCount; series.Name = GetUniqueSeriesName(series); } #region GetUniqueSeriesName private string GetUniqueSeriesName(BaseSeries series) { string name = "Series" + series.SeriesId; while (FindChartSeries(name) != null) name += "*"; return (name); } #endregion #endregion #region SetDataConnection private bool SetDataConnection(object dataSource, bool autogen) { ClearDataNotification(dataSource); if (_BindingSource != null || _ChartControl.BindingContext != null) SetDataNotification(dataSource, autogen); return (_NotificationInProgress == false); } #endregion #region SetDataNotification private void SetDataNotification(object dataSource, bool autogen) { ISupportInitializeNotification notification = dataSource as ISupportInitializeNotification; if (notification == null || notification.IsInitialized == true) { CurrencyManager = (_BindingSource != null) ? _BindingSource.CurrencyManager : _ChartControl.BindingContext[DataView ?? dataSource] as CurrencyManager; } else { CurrencyManager = null; if (_NotificationInProgress == false) { _NotificationInProgress = true; _NotificationAutogen = autogen; notification.Initialized += DataSourceInitialized; } } } #endregion #region ClearDataNotification private void ClearDataNotification(object dataSource) { ISupportInitializeNotification notification = dataSource as ISupportInitializeNotification; if (notification != null && _NotificationInProgress == true) { notification.Initialized -= DataSourceInitialized; _NotificationInProgress = false; _NotificationAutogen = false; } } #endregion #region DataSourceInitialized private void DataSourceInitialized(object sender, EventArgs e) { ISupportInitializeNotification dataSource = _Chart.DataSource as ISupportInitializeNotification; if (dataSource != null) { _NotificationInProgress = false; dataSource.Initialized -= DataSourceInitialized; CurrencyManager = _ChartControl.BindingContext[dataSource] as CurrencyManager; if (dataSource is DataTable) ProcessAddDataTable((DataTable)dataSource, _NotificationAutogen); else ProcessAddList(dataSource, _NotificationAutogen); } } #endregion #region GetPdc private PropertyDescriptorCollection GetPdc(object dataItem) { if (_Chart.EnableDiscreteBoundItems == false) return (_Pdc); if (dataItem != null) return (TypeDescriptor.GetProperties(dataItem.GetType())); return (null); } #endregion #region FindPropertyDescriptor private PropertyDescriptor FindPropertyDescriptor( PropertyDescriptorCollection pdc, string name) { if (pdc != null) { foreach (PropertyDescriptor pd in pdc) { if (pd.Name.Equals(name) == true) return (pd); } } return (null); } #endregion #region FindChartSeries private BaseSeries FindChartSeries(string name) { ChartBaseSeriesCollection baseSeries = _Chart.BaseSeries; foreach (BaseSeries series in baseSeries) { if (name.Equals(series.Name) == true) return (series); } return (null); } #endregion #region LoadSeriesData internal void LoadSeriesData(BaseSeries series) { if (_BaseDataSource is DataSet) LoadDataSetTable((DataSet)_BaseDataSource, _BaseDataMember, series); else if (_BaseDataSource is DataTable) LoadDataTableSeriesData((DataTable)_BaseDataSource, series); else LoadListSeriesData(series); } #region LoadDataSetTable internal void LoadDataSetTable(DataSet dataSet, string dataMember, BaseSeries series) { if (string.IsNullOrEmpty(dataMember) && dataSet.Tables.Count > 0) dataMember = dataSet.Tables[0].TableName; if (string.IsNullOrEmpty(dataMember) == false) LoadDataTableSeriesData(dataSet.Tables[dataMember], series); } #endregion #region LoadDataTableSeriesData private void LoadDataTableSeriesData(DataTable dt, BaseSeries series) { DataColumn dtSeries = FindDataColumn(dt, series.DataPropertyNameSeries); DataColumn dtNameX = FindDataColumn(dt, series.DataPropertyNameX); if (dtNameX == null) throw new Exception("DataPropertyNameX (" + series.DataPropertyNameX + ") not present in DataTable."); DataColumn[] dtNamesY = new DataColumn[series.DataPropertyNamesY.Count]; for (int i = 0; i < series.DataPropertyNamesY.Count; i++) { string name = series.DataPropertyNamesY[i]; dtNamesY[i] = FindDataColumn(dt, name); if (dtNamesY[i] == null) throw new Exception("DataPropertyNameY (" + name + ") not present in DataTable."); } if (dtSeries == null) LoadDataTableSeriesDataSingle(series, dt, dtNameX, dtNamesY); else LoadDataTableSeriesDataMultiple(series, dt, dtSeries, dtNameX, dtNamesY); _ChartControl.DoSeriesDataBindingCompleteEvent(_Chart, series, dt); } #region LoadDataTableSeriesDataSingle private void LoadDataTableSeriesDataSingle( BaseSeries series, DataTable dt, DataColumn dtNameX, DataColumn[] dtNamesY) { for (int i = 0; i < CurrencyManager.Count; i++) { DataRowView view = CurrencyManager.List[i] as DataRowView; if (view != null) AddDataTableSeriesData(series, dt, dtNameX, dtNamesY, view); } } #endregion #region LoadDataTableSeriesDataMultiple private void LoadDataTableSeriesDataMultiple(BaseSeries series, DataTable dt, DataColumn dtSeries, DataColumn dtNameX, DataColumn[] dtNamesY) { for (int i = 0; i < CurrencyManager.Count; i++) { DataRowView view = CurrencyManager.List[i] as DataRowView; if (view != null) { object seriesKey = view.Row.ItemArray[dtSeries.Ordinal]; if (seriesKey == series.SeriesKey || seriesKey.Equals(series.SeriesKey)) AddDataTableSeriesData(series, dt, dtNameX, dtNamesY, view); } } } #endregion #region AddDataTableSeriesData private void AddDataTableSeriesData(BaseSeries series, DataTable dt, DataColumn dtNameX, DataColumn[] dtNamesY, DataRowView view) { object valueX = view.Row.ItemArray[dtNameX.Ordinal]; object[] valuesY = new object[dtNamesY.Length]; for (int i = 0; i < dtNamesY.Length; i++) { DataColumn dtcol = dtNamesY[i]; valuesY[i] = view.Row.ItemArray[dtcol.Ordinal]; } series.AddSeriesPoint(valueX, valuesY, view); } #endregion #endregion #region LoadListSeriesData private void LoadListSeriesData(BaseSeries series) { object source = _BaseDataSource; PropertyDescriptor pdSeries = FindListPd(series.DataPropertyNameSeries); PropertyDescriptor pdNameX = FindListPd(series.DataPropertyNameX); if (pdNameX == null) throw new Exception("DataPropertyNameX (" + series.DataPropertyNameX + ") not present."); PropertyDescriptor[] pdNamesY = new PropertyDescriptor[series.DataPropertyNamesY.Count]; for (int i = 0; i < series.DataPropertyNamesY.Count; i++) { string name = series.DataPropertyNamesY[i]; pdNamesY[i] = FindListPd(name); if (pdNamesY[i] == null) throw new Exception("DataPropertyNameY (" + name + ") not present."); } if (pdSeries == null) LoadListSeriesDataSingle(series, source, pdNameX, pdNamesY); else LoadListSeriesDataMultiple(series, source, pdSeries, pdNameX, pdNamesY); _ChartControl.DoSeriesDataBindingCompleteEvent(_Chart, series, source); } #region LoadListSeriesDataSingle private void LoadListSeriesDataSingle( BaseSeries series, object source, PropertyDescriptor pdNameX, PropertyDescriptor[] pdNamesY) { IList list = source as IList; if (list != null) { for (int i = 0; i < list.Count; i++) { object o = list[i]; AddListSeriesData(series, o, pdNameX, pdNamesY); } } } #endregion #region LoadListSeriesDataMultiple private void LoadListSeriesDataMultiple(BaseSeries series, object source, PropertyDescriptor pdSeries, PropertyDescriptor pgNameX, PropertyDescriptor[] pdNamesY) { IList list = source as IList; if (list != null) { for (int i = 0; i < list.Count; i++) { object o = list[i]; object seriesKey = pdSeries.GetValue(o); if (seriesKey == series.SeriesKey || seriesKey.Equals(series.SeriesKey)) AddListSeriesData(series, o, pgNameX, pdNamesY); } } } #endregion #region AddListSeriesData private void AddListSeriesData(BaseSeries series, object o, PropertyDescriptor pdNameX, PropertyDescriptor[] pdNamesY) { object valueX = pdNameX.GetValue(o); object[] valuesY = new object[pdNamesY.Length]; for (int i = 0; i < pdNamesY.Length; i++) valuesY[i] = pdNamesY[i].GetValue(o); series.AddSeriesPoint(valueX, valuesY, o); } #endregion #endregion #endregion #region CurrencyManagerListChanged void CurrencyManagerListChanged(object sender, ListChangedEventArgs e) { if (_CurrencyManager != null) _Chart.InvalidateLayout(); } #endregion #region Dispose /// /// Data Binder Dispose /// public void Dispose() { CurrencyManager = null; } #endregion } }