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(); } } } /// /// Creates an instance of where T is only known at run time, /// not compile time /// private static IList CreateBindingList(Type type) { Type genericType = typeof(BindingList<>); Type bindingType = genericType.MakeGenericType(new Type[] { type }); return (IList)Activator.CreateInstance(bindingType); } /// /// Create an object of the given type. Throw an exception if this fails. /// 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; } /// /// 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. /// 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)); } } /// /// 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. /// private static IList GetListFromEnumerable(IEnumerable enumerable) { IList list = null; foreach (object item in enumerable) { list ??= CreateBindingList(item.GetType()); list.Add(item); } return list; } /// /// Binds the BindingSource to the list specified by its DataSource and DataMember /// properties. /// 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); } /// /// Binds the BindingSource to the specified list, rewiring internal event handlers, /// firing any appropriate external events, and updating all relevant field members. /// 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 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); } } }