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);
}
}
}