Why BindingList is slow with objects implementing INotifyPropertyChanged interface

Posted on | March 29, 2012 | No Comments

A BindingList<T> is the main tool for binding objects to grids. It is a container that may notify subscribers when data is added or removed. For this purpose it provides public ListChanged event that specifies collection changes with ListChangedType. Besides changing the collection itself (adding, removing, etc) the binding list notifies of data object changes. In other words, when data is added to collection BindingList<T> checks whether it implements INotifyPropertyChanged interface. If it is implemented, the collection subscribes to each object changes and forwards notifications as IBindingList.ListChanged events with ListChangedType.ItemChanged type and with specified PropertyDescriptor that is searched by reflection.

It’s worth mentioning that handler of INotifyPropertyChanged notification is very poorly implemented. Low performance reflection is used for searching PropertyDescriptor and internal cache is very poorly organized thus significantly reducing performance. Specifically when the BindingList<T> receives notifications of new object, it compares this object with the previous object that was used for notification. If they are not identical, the binding list does a terrible thing – it runs through all objects to get the index of the notifying object. This seriously impacts performance when there are a lot of elements. When the number of elements is about 100 000, handling of a single notification takes about 1 ms, which is unacceptable for most applications.

In actual applications ListChangedType.ItemChanged event with ListChangedType.ItemChanged flag and set PropertyDescriptor is handled quite rarely. However, it is hard to disable INotifyPropertyChanged handling in a BindingList<T> via simple means. The only acceptable method of disabling handling is to use reflection. The thing is that the binding list in its constructor checks whether the data type implements INotifyPropertyChanged interface and sets raiseItemChangedEvents private variable to true if it does. Otherwise it is set to false. This variable influences only subscriptions to INotifyPropertyChanged interface. There is no direct access to this variable and it can be modified only via reflection. An example of code for this is provided below.

class CustomBindingList<T> : BindingList<T>
{
    public CustomBindingList()
    {
        FieldInfo fi = typeof (BindingList<T>).GetField("raiseItemChangedEvents",
                               BindingFlags.Instance | BindingFlags.NonPublic);
        if (fi != null)
        {
            fi.SetValue(this, false);
        }
    }
}


The above implements fully functional binding list with disabled handling of INotifуPropertyChanged interface events. At the same time the binding list notifies subscribers of adding or removing data just like in standard implementation of this container.

Some figures: In standard binding list implementation handling of 1000 notifications in 100 000 container took more than 600 msec. In optimized version this time – 0 since the container no longer subscribes to data objects. Besides that, no subscription enables memory saving.

This article concerns only binding list performance aspects. Another important aspect of working with binding lists INotifyPropertyChanged is their use in advanced binding.

Comments

Leave a Reply