Training



The BindableList(of T) Level 200
DevForce Express
December 21, 2006

DevForce includes a list class, IdeaBlade.Util.BindableList(Of T), that subclasses and extends .NET’s System.ComponentModel.BindingList(Of T). The DevForce BindableList(Of T) augments the functionality of the BindingList in several important ways:

  1. By supporting dynamic properties and altered collections of properties.
  2. By providing enhanced facilities for sorting.
  3. By supporting the use of ListManagers that can discover newly added or altered instances of the contained type and add them to specified lists.
  4. By fixing a BindingList memory leak.

Please read on to learn more about these four areas of enhancement!

Support for Dynamic Properties and Altered Collections of Properties

BindableList(Of T) implements two important interfaces that BindingList does not. One, System.ComponentModel.ITypedList, is a .NET interface; the other, IdeaBlade.Util.IAdaptiveList, is a DevForce interface. The first provides support for the dynamic management of PropertyDescriptors; that is, it provides the foundation for altering the published schemas of items contained in the list. Suppose, for example, that you have a Ball object with properties of Color, Material, and Diameter. But for some particular purpose you want to expose a list of such Ball objects and have the contained objects appear not to have a Color property. For another purpose you want a list of Ball objects that have not only the properties Color, Material, and Diameter, but also a Bounceability property – even though Bounceability isn’t a property that is defined in the Ball class itself. You want the consumers of this second list to find a Bounceability property, and you’re willing to take care of making sure it reports a correct value. You want this special property to be a first-class citizen in every respect, including being one to which a data binding can be set.

ITypedList provides the means to accomplish these objectives. It mandates a GetItemProperties() method that can return whatever collection of properties (or more precisely, PropertyDescriptors) you want consumers to see. However, for this to work, a consumer must use this GetItemProperties() method to obtain the list of properties (rather than using reflection against the contained objects). DevForce binding facilities always use GetItemProperties() to determine what properties are available for binding, and this is the basis for DevForce’s support of deeply nested property references (e.g., anEmployee.Manager.Manager.LastName) and also for dynamic properties. ITypedList is a great start, but it would be of little use without a mechanism for actually altering the list of PropertyDescriptors reported by a list. Left to its own devices, even the BindableList(Of T) will report only (and all of) those PropertyDescriptors that it finds intrinsically associated with its contained type. (That is, for our Ball object it will report Color, Material, and Diameter, the set of properties defined in the Ball class.) But how to add and remove properties? That capability is provided by implementation of the IdeaBlade.Util.IAdaptiveList interface.

IAdaptiveList provides three methods -- AddPropertyDescriptor(), GetPropertyDescriptor(), and RemovePropertyDescriptor() – and a Boolean property, UsesGlobalPropertyDescriptors(). AddPropertyDescriptor() and RemovePropertyDescriptor(), as their names imply, permit PropertyDescriptors to be added to and removed from the collection published by ITypedList.GetItemProperties(). GetPropertyDescriptor() permits you to get the PropertyDescriptor associated with a given property path (like “anEmployee.Manager.Manager.LastName”, the last name of an employee’s boss’s boss).

Enhanced Facilities for Sorting

System.Component.BindingList provides a method, ApplySortCore(), which must be overridden by a derived class to provide sort capability on the list. BindableList(Of T) does override this method, and in addition provides five overloads of an ApplySort method that uses it. You can call ApplySort() with a string-valued property name, a PropertyDescriptor, or a System.Collections.Generic.IComparer(Of T). (The latter permits complex sorts, including multi-column sorts.) Three of the overloads support a pKeepListSorted parameter. By setting this to true, your list stays sorted even as new items are added and existing items are changed!

Support for List Managers

The third pillar of BindableList(of T)’s superiority over BindingList consists in its support for ListManagers. Each BindableList(of T) has a ListManager property which, if defined, is an object instantiated from a class that implements IdeaBlade.Util.IListManager. This ListManager watches some external environment – which might be a file system, the DevForce cache, or anything else – for new instances of the type contained in the BindableList. It checks these new instances – or existing instances whose state has changed – against specified criteria. If the ListManager discovers an instance that meets the criteria, it adds that instance to the BindableList(Of T) that it is managing. A given ListManager can manage multiple BindableLists(Of T), adding instances as needed to all of them. In DevForce, ListManagers are frequently used with EntityLists (which subclass BindableList and are specially designed for use with collections of DevForce-generated business objects) to watch the DevForce PersistenceManager cache. As new business objects appear there that meet specified criteria, they are added to the managed EntityLists.

BindingList Memory Leak

The System.ComponentModel.BindingList shares a general problem related to containers which are consumers of events published by their containees. Naturally, a BindingList, acting as a container, acquires strong references to the objects it contains. But it also listens for a PropertyChanged event on contained objects which (like DevForce business objects) implement the System.ComponentModel.INotifyPropertyChanged interface. In order for the contained objects to be able to notify the containing lists when one of their property values changes, they must themselves hold a strong reference to the containing list. In short, the list holds strong references to the contained objects; and the contained objects each hold a strong reference to the list. It’s like two wrestlers desperately clutching each other by the throat! The container can’t be garbage-collected as long as anything else holds a reference to any of the objects it contains. DevForce’s BindableList prevents this problem by some clever programming. Notification to the lists about property value changes in their contained objects is accomplished by way of an intermediate object. The contained Order objects hold strong references to the intermediate object; but the intermediate object has only a weak reference to the containing list.

Because of that, the (IdeaBlade.Util.BindableList) clone lists are able to be garbage-collected even when other objects continue to hold references to the objects they contain; and minimal memory buildup occurs.

Conclusion

For data binding, the BindableList(Of T) -- and its subclass, the EntityList(Of T) -- gives you a powerful and deep-reaching alternative to the .NET BindingList(Of T). Besides its intrinsic benefits, the BindableList(Of T) is leveraged by other DevForce components, such as the BindingManagers, to provide a remarkably responsive and manageable data binding environment. With an EntityList(), feeding a DevForce BindingManager, you get the full capabilities of DevForce databinding: bi-directional binding, nested properties, dynamic properties, insulation from syntactic idiosyncracies of third-party controls, and a ControlBindingDescriptors collection that gathers all bindings to a given business object type under a single umbrella for easy configuration and maintenance!

All Tech Tips