BindingSource.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. using System.Collections;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.Reflection;
  5. namespace System.Windows.Forms
  6. {
  7. public class BindingSource : Component
  8. {
  9. // Public property values
  10. private object _dataSource;
  11. private string _dataMember = string.Empty;
  12. // Description of the current bound list
  13. private IList _innerList;
  14. public object DataSource
  15. {
  16. get => _dataSource;
  17. set
  18. {
  19. if (_dataSource != value)
  20. {
  21. // Accept the new data source
  22. _dataSource = value;
  23. // Get the inner list for our new DataSource/DataMember combination
  24. ResetList();
  25. }
  26. }
  27. }
  28. /// <summary>
  29. /// Creates an instance of <see cref="BindingList{T}"/> where T is only known at run time,
  30. /// not compile time
  31. /// </summary>
  32. private static IList CreateBindingList(Type type)
  33. {
  34. Type genericType = typeof(BindingList<>);
  35. Type bindingType = genericType.MakeGenericType(new Type[] { type });
  36. return (IList)Activator.CreateInstance(bindingType);
  37. }
  38. /// <summary>
  39. /// Create an object of the given type. Throw an exception if this fails.
  40. /// </summary>
  41. private static object CreateInstanceOfType(Type type)
  42. {
  43. object instancedObject = null;
  44. Exception instanceException = null;
  45. try
  46. {
  47. instancedObject = Activator.CreateInstance(type);
  48. }
  49. catch (TargetInvocationException ex)
  50. {
  51. instanceException = ex; // Default ctor threw an exception
  52. }
  53. catch (MethodAccessException ex)
  54. {
  55. instanceException = ex; // Default ctor was not public
  56. }
  57. catch (MissingMethodException ex)
  58. {
  59. instanceException = ex; // No default ctor defined
  60. }
  61. if (instanceException is not null)
  62. {
  63. throw new NotSupportedException("BindingSource Instance Error", instanceException);
  64. }
  65. return instancedObject;
  66. }
  67. /// <summary>
  68. /// Given a type, create a list based on that type. If the type represents a list type,
  69. /// we create an instance of that type (or throw if we cannot instance that type).
  70. /// Otherwise we assume the type represents the item type, in which case we create
  71. /// a typed BindingList of that item type.
  72. /// </summary>
  73. private static IList GetListFromType(Type type)
  74. {
  75. if (typeof(ITypedList).IsAssignableFrom(type) && typeof(IList).IsAssignableFrom(type))
  76. {
  77. return CreateInstanceOfType(type) as IList;
  78. }
  79. else if (typeof(IListSource).IsAssignableFrom(type))
  80. {
  81. return (CreateInstanceOfType(type) as IListSource).GetList();
  82. }
  83. else
  84. {
  85. return CreateBindingList(ListBindingHelper.GetListItemType(type));
  86. }
  87. }
  88. /// <summary>
  89. /// Creates a list based on an enumerable object. We rip through the enumerable,
  90. /// extract all its items, and stuff these items into a typed BindingList, using
  91. /// the type of the first item to determine the type of the list.
  92. /// </summary>
  93. private static IList GetListFromEnumerable(IEnumerable enumerable)
  94. {
  95. IList list = null;
  96. foreach (object item in enumerable)
  97. {
  98. list ??= CreateBindingList(item.GetType());
  99. list.Add(item);
  100. }
  101. return list;
  102. }
  103. /// <summary>
  104. /// Binds the BindingSource to the list specified by its DataSource and DataMember
  105. /// properties.
  106. /// </summary>
  107. private void ResetList()
  108. {
  109. // Find the list identified by the current DataSource and DataMember properties.
  110. //
  111. // If the DataSource only specifies a Type, we actually create an
  112. // instance from that Type and obtain the list from that instance.
  113. //
  114. // Note: The method below will throw an exception if a data member is specified
  115. // but does not correspond to a valid property on the data source.
  116. object dataSourceInstance = _dataSource is Type dataSourceType ? GetListFromType(dataSourceType) : _dataSource;
  117. object list = ListBindingHelper.GetList(dataSourceInstance, _dataMember);
  118. // Convert the candidate list into an IList, if necessary...
  119. IList bindingList = null;
  120. if (list is IList iList)
  121. {
  122. // If its already an IList then we're done!
  123. bindingList = iList;
  124. }
  125. else
  126. {
  127. if (list is IListSource iListSource)
  128. {
  129. bindingList = iListSource.GetList();
  130. }
  131. else if (list is IEnumerable iEnumerable)
  132. {
  133. // If its an enumerable list, extract its contents and put them in a new list
  134. bindingList = GetListFromEnumerable(iEnumerable);
  135. }
  136. // If it's not an IList, IListSource or IEnumerable
  137. if (bindingList is null)
  138. {
  139. if (list is not null)
  140. {
  141. // If its some random non-list object, just wrap it in a list
  142. bindingList = WrapObjectInBindingList(list);
  143. }
  144. else
  145. {
  146. // Can't get any list from the data source (eg. data member specifies related sub-list but the
  147. // data source's list is empty, so there is no current item to get the sub-list from). In this
  148. // case we simply determine what the list's item type would be, and create empty list with that
  149. // same item type. If the item type cannot be determined, we end up with an item type of 'Object'.
  150. Type type = ListBindingHelper.GetListItemType(_dataSource, _dataMember);
  151. bindingList = GetListFromType(type);
  152. bindingList ??= CreateBindingList(type);
  153. }
  154. }
  155. }
  156. // Bind to this list now
  157. SetList(bindingList, metaDataChanged: true, applySortAndFilter: true);
  158. }
  159. /// <summary>
  160. /// Binds the BindingSource to the specified list, rewiring internal event handlers,
  161. /// firing any appropriate external events, and updating all relevant field members.
  162. /// </summary>
  163. private void SetList(IList list, bool metaDataChanged, bool applySortAndFilter)
  164. {
  165. if (list is null)
  166. {
  167. // The list argument should never be null! We will handle null gracefully
  168. // at run-time, but we will complain bitterly about this in debug builds!
  169. Debug.Fail("BindingSource.SetList() was called with illegal <null> list argument!");
  170. }
  171. // Bind to the new list
  172. if (!(ListBindingHelper.GetList(list) is IList listInternal))
  173. {
  174. listInternal = list;
  175. }
  176. _innerList = listInternal;
  177. }
  178. private static IList WrapObjectInBindingList(object obj)
  179. {
  180. IList list = CreateBindingList(obj.GetType());
  181. list.Add(obj);
  182. return list;
  183. }
  184. public virtual PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
  185. {
  186. return ListBindingHelper.GetListItemProperties(_innerList, listAccessors);
  187. }
  188. }
  189. }