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:
- By supporting dynamic properties
and altered collections of
properties.
- By providing enhanced facilities
for sorting.
- By supporting the use of
ListManagers that can discover
newly added or altered instances
of the contained type and add
them to specified lists.
- 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!
|