563 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			563 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Text;
 | |
| using System.ComponentModel;
 | |
| using System.Collections;
 | |
| using System.Windows.Forms;
 | |
| using System.Reflection;
 | |
| using System.Globalization;
 | |
| using DevComponents.DotNetBar.Primitives;
 | |
| 
 | |
| namespace DevComponents.DotNetBar
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Provides binding support for ItemPanel control.
 | |
|     /// </summary>
 | |
|     public class ItemVisualGenerator
 | |
|     {
 | |
|         #region Constructor
 | |
|         private IBindingSupport _Parent = null;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes a new instance of the ItemVisualGenerator class.
 | |
|         /// </summary>
 | |
|         /// <param name="parent"></param>
 | |
|         public ItemVisualGenerator(IBindingSupport parent)
 | |
|         {
 | |
|             _Parent = parent;
 | |
|         }
 | |
| 
 | |
|         #endregion
 | |
| 
 | |
|         #region Implementation
 | |
|         private object _DataSource = null;
 | |
|         /// <summary>
 | |
|         /// Gets or sets the data source for the ComboTree. Expected is an object that implements the IList or IListSource interfaces, 
 | |
|         /// such as a DataSet or an Array. The default is null.
 | |
|         /// </summary>
 | |
|         [AttributeProvider(typeof(IListSource)), Description("Indicates data source for the ComboTree."), Category("Data"), DefaultValue(null), RefreshProperties(RefreshProperties.Repaint)]
 | |
|         public object DataSource
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return _DataSource;
 | |
|             }
 | |
|             set
 | |
|             {
 | |
|                 if (((value != null) && !(value is IList)) && !(value is IListSource))
 | |
|                 {
 | |
|                     throw new ArgumentException("Data type is not supported for complex data binding");
 | |
|                 }
 | |
|                 if (_DataSource != value)
 | |
|                 {
 | |
|                     if (DesignMode)
 | |
|                         _DataSource = value;
 | |
|                     else
 | |
|                         this.SetDataConnection(value, true);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private bool DesignMode
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (_Parent is IComponent && ((IComponent)_Parent).Site != null)
 | |
|                     return ((IComponent)_Parent).Site.DesignMode;
 | |
|                 if (_Parent is Control && ((Control)_Parent).Site != null)
 | |
|                     return ((Control)_Parent).Site.DesignMode;
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private bool _InSetDataConnection = false;
 | |
|         private void SetDataConnection(object newDataSource, bool force)
 | |
|         {
 | |
|             bool dataSourceChanged = _DataSource != newDataSource;
 | |
| 
 | |
|             if (!_InSetDataConnection)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (force || dataSourceChanged)
 | |
|                     {
 | |
|                         _InSetDataConnection = true;
 | |
|                         IList list = (this.DataManager != null) ? this.DataManager.List : null;
 | |
|                         bool isDataManagerNull = this.DataManager == null;
 | |
|                         this.UnwireDataSource();
 | |
|                         _DataSource = newDataSource;
 | |
|                         this.WireDataSource();
 | |
|                         if (_IsDataSourceInitialized && !IsCustomCollection)
 | |
|                         {
 | |
|                             CurrencyManager manager = null;
 | |
|                             if (((newDataSource != null) && (_Parent.BindingContext != null)) && (newDataSource != Convert.DBNull))
 | |
|                             {
 | |
|                                 string bindingPath = "";
 | |
|                                 if (_Bindings != null && _Bindings.Count > 0)
 | |
|                                     bindingPath = _Bindings[0].BindingMemberInfo.BindingPath;
 | |
|                                 manager = (CurrencyManager)_Parent.BindingContext[newDataSource, bindingPath];
 | |
|                             }
 | |
|                             if (_DataManager != manager)
 | |
|                             {
 | |
|                                 if (_DataManager != null)
 | |
|                                 {
 | |
|                                     _DataManager.ItemChanged -= new ItemChangedEventHandler(this.DataManager_ItemChanged);
 | |
|                                     _DataManager.PositionChanged -= new EventHandler(this.DataManager_PositionChanged);
 | |
|                                 }
 | |
|                                 _DataManager = manager;
 | |
|                                 if (_DataManager != null)
 | |
|                                 {
 | |
|                                     _DataManager.ItemChanged += new ItemChangedEventHandler(this.DataManager_ItemChanged);
 | |
|                                     _DataManager.PositionChanged += new EventHandler(this.DataManager_PositionChanged);
 | |
|                                 }
 | |
|                             }
 | |
|                             if (((_DataManager != null) && (dataSourceChanged)) && !ValidateDisplayMembers(_Bindings))
 | |
|                             {
 | |
|                                 throw new ArgumentException("Wrong Bindings parameter", "newBindings");
 | |
|                             }
 | |
|                             if ((_DataManager != null && (dataSourceChanged || force))
 | |
|                                 && (dataSourceChanged || (force && ((list != _DataManager.List) || isDataManagerNull))))
 | |
|                             {
 | |
|                                 DataManager_ItemChanged(_DataManager, null);
 | |
|                             }
 | |
|                         }
 | |
|                         _Converters.Clear();
 | |
|                     }
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     _InSetDataConnection = false;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private bool ValidateDisplayMembers(List<BindingDef> members)
 | |
|         {
 | |
|             if (members == null || members.Count == 0) return true;
 | |
| 
 | |
|             foreach (BindingDef item in members)
 | |
|             {
 | |
|                 if (item.BindingMemberInfo != null && !BindingMemberInfoInDataManager(item.BindingMemberInfo))
 | |
|                     return false;
 | |
|             }
 | |
|             return true;
 | |
|         }
 | |
|         private bool BindingMemberInfoInDataManager(BindingMemberInfo bindingMemberInfo)
 | |
|         {
 | |
|             if (_DataManager != null)
 | |
|             {
 | |
|                 PropertyDescriptorCollection itemProperties = _DataManager.GetItemProperties();
 | |
|                 int count = itemProperties.Count;
 | |
|                 for (int i = 0; i < count; i++)
 | |
|                 {
 | |
|                     if (!typeof(IList).IsAssignableFrom(itemProperties[i].PropertyType) && itemProperties[i].Name.Equals(bindingMemberInfo.BindingField))
 | |
|                     {
 | |
|                         return true;
 | |
|                     }
 | |
|                 }
 | |
|                 for (int j = 0; j < count; j++)
 | |
|                 {
 | |
|                     if (!typeof(IList).IsAssignableFrom(itemProperties[j].PropertyType) && (string.Compare(itemProperties[j].Name, bindingMemberInfo.BindingField, true, CultureInfo.CurrentCulture) == 0))
 | |
|                     {
 | |
|                         return true;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         private List<BindingDef> _Bindings = new List<BindingDef>();
 | |
|         public List<BindingDef> Bindings
 | |
|         {
 | |
|             get { return _Bindings; }
 | |
|             set
 | |
|             {
 | |
|                 if (value != _Bindings)
 | |
|                 {
 | |
|                     List<BindingDef> oldValue = _Bindings;
 | |
|                     _Bindings = value;
 | |
|                     OnBindingsChanged(oldValue, value);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Called when Bindings property has changed.
 | |
|         /// </summary>
 | |
|         /// <param name="oldValue">Old property value</param>
 | |
|         /// <param name="newValue">New property value</param>
 | |
|         protected virtual void OnBindingsChanged(List<BindingDef> oldValue, List<BindingDef> newValue)
 | |
|         {
 | |
|             //OnPropertyChanged(new PropertyChangedEventArgs("Bindings"));
 | |
|         }
 | |
| 
 | |
|         private CurrencyManager _DataManager = null;
 | |
|         internal CurrencyManager DataManager
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return _DataManager;
 | |
|             }
 | |
|         }
 | |
|         private bool _IsDataSourceInitEventHooked = false;
 | |
|         private void UnwireDataSource()
 | |
|         {
 | |
|             if (IsCustomCollection)
 | |
|             {
 | |
|                 CustomCollection<object> col = (CustomCollection<object>)_DataSource;
 | |
|                 col.CollectionChanged -= CustomCollectionChanged;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 if (_DataSource is IComponent)
 | |
|                 {
 | |
|                     ((IComponent) _DataSource).Disposed -= new EventHandler(DataSourceDisposed);
 | |
|                 }
 | |
|                 ISupportInitializeNotification dataSource = _DataSource as ISupportInitializeNotification;
 | |
|                 if ((dataSource != null) && _IsDataSourceInitEventHooked)
 | |
|                 {
 | |
|                     dataSource.Initialized -= new EventHandler(DataSourceInitialized);
 | |
|                     _IsDataSourceInitEventHooked = false;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void CustomCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
 | |
|         {
 | |
|             _Parent.BeginUpdate();
 | |
|             try
 | |
|             {
 | |
|                 if (e.Action == NotifyCollectionChangedAction.Add)
 | |
|                 {
 | |
|                     int i = e.NewStartingIndex;
 | |
|                     foreach (object item in e.NewItems)
 | |
|                     {
 | |
|                         CreateVisualItem(_Parent.ItemsCollection, item, i, _Bindings);
 | |
|                         i++;
 | |
|                     }
 | |
|                 }
 | |
|                 else if (e.Action == NotifyCollectionChangedAction.Remove)
 | |
|                 {
 | |
|                     _Parent.ItemsCollection.RemoveAt(e.OldStartingIndex);
 | |
|                     //_Parent.ItemsCollection.RemoveAt(e.NewStartingIndex);
 | |
|                 }
 | |
|                 else if (e.Action == NotifyCollectionChangedAction.Replace)
 | |
|                 {
 | |
|                     SetItemCore(e.NewStartingIndex, e.NewItems[0]);
 | |
|                 }
 | |
|                 else if (e.Action == NotifyCollectionChangedAction.Reset)
 | |
|                 {
 | |
|                     RefreshItems();
 | |
|                 }
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 _Parent.EndUpdate();
 | |
|             }
 | |
|         }
 | |
|         private void DataSourceDisposed(object sender, EventArgs e)
 | |
|         {
 | |
|             this.SetDataConnection(null, true);
 | |
|         }
 | |
|         private bool _IsDataSourceInitialized = false;
 | |
|         private void WireDataSource()
 | |
|         {
 | |
|             if (IsCustomCollection)
 | |
|             {
 | |
|                 CustomCollection<object> col = (CustomCollection<object>) _DataSource;
 | |
|                 col.CollectionChanged += CustomCollectionChanged;
 | |
|                 _IsDataSourceInitialized = true;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 if (_DataSource is IComponent)
 | |
|                 {
 | |
|                     ((IComponent) _DataSource).Disposed += new EventHandler(DataSourceDisposed);
 | |
|                 }
 | |
|                 ISupportInitializeNotification dataSource = _DataSource as ISupportInitializeNotification;
 | |
|                 if ((dataSource != null) && !dataSource.IsInitialized)
 | |
|                 {
 | |
|                     dataSource.Initialized += new EventHandler(DataSourceInitialized);
 | |
|                     _IsDataSourceInitEventHooked = true;
 | |
|                     _IsDataSourceInitialized = false;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     _IsDataSourceInitialized = true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         private void DataSourceInitialized(object sender, EventArgs e)
 | |
|         {
 | |
|             this.SetDataConnection(_DataSource, true);
 | |
|         }
 | |
| 
 | |
|         private void DataManager_ItemChanged(object sender, ItemChangedEventArgs e)
 | |
|         {
 | |
|             if (_Parent is Control)
 | |
|             {
 | |
|                 Control parentControl = (Control)_Parent;
 | |
|                 if (parentControl.InvokeRequired)
 | |
|                 {
 | |
|                     parentControl.Invoke(new ItemChangedEventHandler(DataManager_ItemChanged), sender, e);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             if (_DataManager != null)
 | |
|             {
 | |
|                 if (e == null || e.Index == -1)
 | |
|                 {
 | |
|                     this.SetItemsCore(_DataManager.List);
 | |
|                     if (_Parent.AllowSelection)
 | |
|                     {
 | |
|                         _Parent.SelectedIndex = _DataManager.Position;
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     this.SetItemCore(e.Index, _DataManager.List[e.Index]);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         private void DataManager_PositionChanged(object sender, EventArgs e)
 | |
|         {
 | |
|             if ((_DataManager != null) && _Parent.AllowSelection)
 | |
|             {
 | |
|                 _Parent.SelectedIndex = _DataManager.Position;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private ICloneable _VisualTemplate = null;
 | |
|         /// <summary>
 | |
|         /// Gets or sets the visual template that is generated for each data item.
 | |
|         /// </summary>
 | |
|         public ICloneable VisualTemplate
 | |
|         {
 | |
|             get { return _VisualTemplate; }
 | |
|             set
 | |
|             {
 | |
|                 if (value != _VisualTemplate)
 | |
|                 {
 | |
|                     ICloneable oldValue = _VisualTemplate;
 | |
|                     _VisualTemplate = value;
 | |
|                     OnVisualTemplateChanged(oldValue, value);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Called when VisualTemplate property has changed.
 | |
|         /// </summary>
 | |
|         /// <param name="oldValue">Old property value</param>
 | |
|         /// <param name="newValue">New property value</param>
 | |
|         protected virtual void OnVisualTemplateChanged(ICloneable oldValue, ICloneable newValue)
 | |
|         {
 | |
|             //OnPropertyChanged(new PropertyChangedEventArgs("VisualTemplate"));
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// When overridden in a derived class, sets the specified array of objects in a collection in the derived class.
 | |
|         /// </summary>
 | |
|         /// <param name="items">An array of items.</param>
 | |
|         protected virtual void SetItemsCore(IList items)
 | |
|         {
 | |
|             _Parent.BeginUpdate();
 | |
| 
 | |
|             _Parent.ItemsCollection.Clear();
 | |
| 
 | |
|             for (int i = 0; i < items.Count; i++)
 | |
|             {
 | |
|                 object item = items[i];
 | |
|                 CreateVisualItem(_Parent.ItemsCollection, item, i, _Bindings);
 | |
|             }
 | |
| 
 | |
|             _Parent.EndUpdate();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a new item from template for the data.
 | |
|         /// </summary>
 | |
|         /// <param name="item">Data to create item for.</param>
 | |
|         /// <returns>New instance of the BaseItem.</returns>
 | |
|         private object CreateVisualItem(IList parent, object item, int itemIndex, List<BindingDef> bindings)
 | |
|         {
 | |
|             if(IsCustomCollection && item is ListBoxItem)
 | |
|             {
 | |
|                 if (parent.Count <= itemIndex)
 | |
|                     parent.Add(item);
 | |
|                 else
 | |
|                     parent.Insert(itemIndex, item);
 | |
| 
 | |
|                 if (bindings.Count == 0) return item;
 | |
|                 SetVisualItemData(item, item, bindings, itemIndex);
 | |
|                 return item;
 | |
|             }
 | |
|             object visualItem = _VisualTemplate.Clone();
 | |
| 
 | |
|             if (parent.Count <= itemIndex)
 | |
|                 parent.Add(visualItem);
 | |
|             else
 | |
|                 parent.Insert(itemIndex, visualItem);
 | |
| 
 | |
|             SetVisualItemData(visualItem, item, bindings, itemIndex);
 | |
| 
 | |
|             DataItemVisualEventArgs eventArgs = new DataItemVisualEventArgs(visualItem, item);
 | |
|             _Parent.InvokeDataNodeCreated(eventArgs);
 | |
| 
 | |
|             return eventArgs.Visual;
 | |
|         }
 | |
| 
 | |
|         private void SetVisualItemData(object visualItem, object item, List<BindingDef> bindings, int bindingIndex)
 | |
|         {
 | |
|             PropertyInfo tagProperty = visualItem.GetType().GetProperty("Tag");
 | |
|             if (tagProperty != null)
 | |
|                 tagProperty.SetValue(visualItem, new ItemBindingData(item, bindingIndex), null);
 | |
| 
 | |
|             if (bindings.Count == 0)
 | |
|             {
 | |
|                 if (visualItem is BaseItem)
 | |
|                 {
 | |
|                     if (item is string)
 | |
|                         ((BaseItem)visualItem).Text = (string)item;
 | |
|                     else if (item != null)
 | |
|                         ((BaseItem)visualItem).Text = item.ToString();
 | |
|                 }
 | |
|                 
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 foreach (BindingDef binding in bindings)
 | |
|                 {
 | |
|                     binding.Update(this, visualItem, item);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// When overridden in a derived class, sets the object with the specified index in the derived class.
 | |
|         /// </summary>
 | |
|         /// <param name="index">The array index of the object.</param>
 | |
|         /// <param name="value">The object.</param>
 | |
|         protected virtual void SetItemCore(int index, object value)
 | |
|         {
 | |
|             object visual = _Parent.ItemsCollection[index];
 | |
|             if (visual == null) return;
 | |
| 
 | |
|             _Parent.BeginUpdate();
 | |
|             SetVisualItemData(visual, value, _Bindings, index);
 | |
|             _Parent.EndUpdate();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// When overridden in a derived class, resynchronizes the item data with the contents of the data source.
 | |
|         /// </summary>
 | |
|         public virtual void RefreshItems()
 | |
|         {
 | |
|             _Parent.ItemsCollection.Clear();
 | |
| 
 | |
|             if (_DataManager != null)
 | |
|             {
 | |
|                 SetItemsCore(_DataManager.List);
 | |
|                 if (_Parent.AllowSelection)
 | |
|                 {
 | |
|                     _Parent.SelectedIndex = _DataManager.Position;
 | |
|                 }
 | |
|             }
 | |
|             else if (_DataSource is IList)
 | |
|                 SetItemsCore((IList)_DataSource);
 | |
|         }
 | |
| 
 | |
|         private Hashtable _Converters = new Hashtable();
 | |
|         internal TypeConverter GetFieldConverter(string fieldName)
 | |
|         {
 | |
|             if (_Converters.ContainsKey(fieldName))
 | |
|                 return (TypeConverter)_Converters[fieldName];
 | |
|             if (_DataManager != null)
 | |
|             {
 | |
|                 PropertyDescriptorCollection itemProperties = _DataManager.GetItemProperties();
 | |
|                 if (itemProperties != null)
 | |
|                 {
 | |
|                     PropertyDescriptor descriptor = itemProperties.Find(fieldName, true);
 | |
|                     if (descriptor != null)
 | |
|                     {
 | |
|                         _Converters.Add(fieldName, descriptor.Converter);
 | |
|                         return descriptor.Converter;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         private bool _CustomCollectionBinding = false;
 | |
|         /// <summary>
 | |
|         /// Indicates that generator should recognize when CustomCollection<T> is set as DataSource and bind to it instead of going through DataManager route.
 | |
|         /// </summary>
 | |
|         public bool CustomCollectionBinding 
 | |
|         {
 | |
|             get { return _CustomCollectionBinding; } 
 | |
|             set
 | |
|             {
 | |
|                 _CustomCollectionBinding = value;
 | |
|             }
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         public bool IsCustomCollection
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return _CustomCollectionBinding && _DataSource is CustomCollection<object>;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public interface IBindingSupport
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Gets or sets whether selection is allowed.
 | |
|         /// </summary>
 | |
|         bool AllowSelection { get;set;}
 | |
|         /// <summary>
 | |
|         /// Gets or sets the selected item index.
 | |
|         /// </summary>
 | |
|         int SelectedIndex { get;set;}
 | |
|         /// <summary>
 | |
|         /// Gets or sets the BindingContext.
 | |
|         /// </summary>
 | |
|         BindingContext BindingContext { get;set;}
 | |
|         IList ItemsCollection { get; }
 | |
|         void BeginUpdate();
 | |
|         void EndUpdate();
 | |
|         void InvokeDataNodeCreated(DataItemVisualEventArgs e);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Defines delegate for data visual creation based events.
 | |
|     /// </summary>
 | |
|     public delegate void DataItemVisualEventHandler(object sender, DataItemVisualEventArgs e);
 | |
|     /// <summary>
 | |
|     /// Defines event arguments for data visual creation based events.
 | |
|     /// </summary>
 | |
|     public class DataItemVisualEventArgs : EventArgs
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Gets or sets the visual that is created for data item.
 | |
|         /// </summary>
 | |
|         public object Visual = null;
 | |
|         /// <summary>
 | |
|         /// Gets the data-item node is being created for.
 | |
|         /// </summary>
 | |
|         public readonly object DataItem = null;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes a new instance of the DataNodeEventArgs class.
 | |
|         /// </summary>
 | |
|         /// <param name="node"></param>
 | |
|         /// <param name="dataItem"></param>
 | |
|         public DataItemVisualEventArgs(object visual, object dataItem)
 | |
|         {
 | |
|             Visual = visual;
 | |
|             DataItem = dataItem;
 | |
|         }
 | |
|     }
 | |
| }
 |