123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- using System.Collections;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Reflection;
- namespace System.Windows.Forms
- {
- public class BindingSource : Component
- {
- // Public property values
- private object _dataSource;
- private string _dataMember = string.Empty;
- // Description of the current bound list
- private IList _innerList;
- public object DataSource
- {
- get => _dataSource;
- set
- {
- if (_dataSource != value)
- {
- // Accept the new data source
- _dataSource = value;
- // Get the inner list for our new DataSource/DataMember combination
- ResetList();
- }
- }
- }
- /// <summary>
- /// Creates an instance of <see cref="BindingList{T}"/> where T is only known at run time,
- /// not compile time
- /// </summary>
- private static IList CreateBindingList(Type type)
- {
- Type genericType = typeof(BindingList<>);
- Type bindingType = genericType.MakeGenericType(new Type[] { type });
- return (IList)Activator.CreateInstance(bindingType);
- }
- /// <summary>
- /// Create an object of the given type. Throw an exception if this fails.
- /// </summary>
- private static object CreateInstanceOfType(Type type)
- {
- object instancedObject = null;
- Exception instanceException = null;
- try
- {
- instancedObject = Activator.CreateInstance(type);
- }
- catch (TargetInvocationException ex)
- {
- instanceException = ex; // Default ctor threw an exception
- }
- catch (MethodAccessException ex)
- {
- instanceException = ex; // Default ctor was not public
- }
- catch (MissingMethodException ex)
- {
- instanceException = ex; // No default ctor defined
- }
- if (instanceException is not null)
- {
- throw new NotSupportedException("BindingSource Instance Error", instanceException);
- }
- return instancedObject;
- }
- /// <summary>
- /// Given a type, create a list based on that type. If the type represents a list type,
- /// we create an instance of that type (or throw if we cannot instance that type).
- /// Otherwise we assume the type represents the item type, in which case we create
- /// a typed BindingList of that item type.
- /// </summary>
- private static IList GetListFromType(Type type)
- {
- if (typeof(ITypedList).IsAssignableFrom(type) && typeof(IList).IsAssignableFrom(type))
- {
- return CreateInstanceOfType(type) as IList;
- }
- else if (typeof(IListSource).IsAssignableFrom(type))
- {
- return (CreateInstanceOfType(type) as IListSource).GetList();
- }
- else
- {
- return CreateBindingList(ListBindingHelper.GetListItemType(type));
- }
- }
- /// <summary>
- /// Creates a list based on an enumerable object. We rip through the enumerable,
- /// extract all its items, and stuff these items into a typed BindingList, using
- /// the type of the first item to determine the type of the list.
- /// </summary>
- private static IList GetListFromEnumerable(IEnumerable enumerable)
- {
- IList list = null;
- foreach (object item in enumerable)
- {
- list ??= CreateBindingList(item.GetType());
- list.Add(item);
- }
- return list;
- }
- /// <summary>
- /// Binds the BindingSource to the list specified by its DataSource and DataMember
- /// properties.
- /// </summary>
- private void ResetList()
- {
- // Find the list identified by the current DataSource and DataMember properties.
- //
- // If the DataSource only specifies a Type, we actually create an
- // instance from that Type and obtain the list from that instance.
- //
- // Note: The method below will throw an exception if a data member is specified
- // but does not correspond to a valid property on the data source.
- object dataSourceInstance = _dataSource is Type dataSourceType ? GetListFromType(dataSourceType) : _dataSource;
- object list = ListBindingHelper.GetList(dataSourceInstance, _dataMember);
- // Convert the candidate list into an IList, if necessary...
- IList bindingList = null;
- if (list is IList iList)
- {
- // If its already an IList then we're done!
- bindingList = iList;
- }
- else
- {
- if (list is IListSource iListSource)
- {
- bindingList = iListSource.GetList();
- }
- else if (list is IEnumerable iEnumerable)
- {
- // If its an enumerable list, extract its contents and put them in a new list
- bindingList = GetListFromEnumerable(iEnumerable);
- }
- // If it's not an IList, IListSource or IEnumerable
- if (bindingList is null)
- {
- if (list is not null)
- {
- // If its some random non-list object, just wrap it in a list
- bindingList = WrapObjectInBindingList(list);
- }
- else
- {
- // Can't get any list from the data source (eg. data member specifies related sub-list but the
- // data source's list is empty, so there is no current item to get the sub-list from). In this
- // case we simply determine what the list's item type would be, and create empty list with that
- // same item type. If the item type cannot be determined, we end up with an item type of 'Object'.
- Type type = ListBindingHelper.GetListItemType(_dataSource, _dataMember);
- bindingList = GetListFromType(type);
- bindingList ??= CreateBindingList(type);
- }
- }
- }
- // Bind to this list now
- SetList(bindingList, metaDataChanged: true, applySortAndFilter: true);
- }
- /// <summary>
- /// Binds the BindingSource to the specified list, rewiring internal event handlers,
- /// firing any appropriate external events, and updating all relevant field members.
- /// </summary>
- private void SetList(IList list, bool metaDataChanged, bool applySortAndFilter)
- {
- if (list is null)
- {
- // The list argument should never be null! We will handle null gracefully
- // at run-time, but we will complain bitterly about this in debug builds!
- Debug.Fail("BindingSource.SetList() was called with illegal <null> list argument!");
- }
- // Bind to the new list
- if (!(ListBindingHelper.GetList(list) is IList listInternal))
- {
- listInternal = list;
- }
- _innerList = listInternal;
- }
- private static IList WrapObjectInBindingList(object obj)
- {
- IList list = CreateBindingList(obj.GetType());
- list.Add(obj);
- return list;
- }
- public virtual PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
- {
- return ListBindingHelper.GetListItemProperties(_innerList, listAccessors);
- }
- }
- }
|