This scenario works in most cases but sometimes it would be beneficial to postpone notifications until later or temporarily disable them all together. For example, until a batch update is finished. This notification delay could increase performance as well as eliminate screen flicker of updated visuals. Unfortunately, the default implementation of ObservableCollection does not provide this functionality.

ObservableCollectionEx is designed to provide this missing functionality. ObservableCollectionEx is designed as a direct replacement for ObservableCollection, is completely code compatible with it, and also provides a way to delay or disable notifications.

Background

In order to postpone notifications, we have to temporarily reroute them to a holding place and fire them all once delay is no longer required. At the same time, we need to continue to provide normal behavior and notifications for other consumers of the collection which do not require delay.

This could be achieved by providing multiple shells which manipulate the same collection. One instance of the shell will contain the element’s container and be a host for all of the notification events, and other instances of the shell will handle disabled and delayed events. These extra shells reference the same container but instead of firing change events they will collect these events and fire them once shell is released.

The ObservableCollection implementation is based on a Collection class which implements ICollection manipulation and ObservableCollection implements notifications. The Collection class is implemented as a shell around the IList interface. It contains a reference to a container which exposes the IList and manipulates this container through it. One of the constructors of the Collection class takes List as a parameter and allows this list to be a container for that Collection. This creates a way to have multiple Collection instances to manipulate the same container, which perfectly serves our purpose.

Unfortunately, this ability is lost in the ObservableCollection implementation. Instead of assigning IList to be a container for the instance, it creates a copy of that List and uses that copy to store elements. This limitation prevents us from inheriting from the ObservableCollection class.

ObservableCollectionEx is based on a Collection class (same as bservableCollection), and implements exactly the same methods and properties as ObservableCollection.

In addition to these members, ObservableCollectionEx exposes two methods to create disabled or delayed notification shells around the container. Methods of the shell created by DisableNotifications() produce no notifications on either INotifyPropertyChanged or INotifyCollectionChanged.

Calls to the methods of the shell created by DelayNorifications() produce no notifications until this instance goes out of scope or IDisposable.Dispose() has been called on it.

How it works

Except for a few performance tricks, ObservableCollectionEx behaves exactly as the ObservableCollection class. It uses Collection to perform its operations, notifies consumers via INotifyPropertyChanged and INotifyCollectionChanged, and creates a copy of the List if you pass it to a constructor.

The differences starts when the DelayNotifications() or DisableNotifications() methods are called. This method creates a new instance of the ObservableCollectionEx object and passes its constructor a reference to the original ObservableCollectionEx object, and the Boolean parameter that specifies if notifications are disabled or postponed. This new instance will have the same interface as the original, the same element’s container, but none of the consumer handlers attached to the CollectionChanged event. So when methods of this instance are called and events are fired, none of these are going anywhere but to temporary storage.

Once updates are done, and either this instance goes out of scope or Dispose() has been called, all of the collected events are combined into one and fired on CollectionChanged and PropertyChanged of the original object notifying all of the consumers about changes.

Implementation

Generally there are two ways to implement notification delays: Subclass ObservableCollection or create ExtensionMethod which operates on regular ObservableCollection. Creating extension method is attractive. It would allow adding delays to any observable collection without much code modifications. But there are price to pay. In order for us to create desired behaviour we require access to two private members of the ObservableCollection class: List collection and PropertyChanged event. Using Reflection we could get to these members but it would not be very elegant solution. It would not be the fastest approach either.Because of these reasons I’ve chosen to implement delays by inheriting from ObservableCollection.

Using the code

The easiest way to include this class into your project is by installing the Nuget package available at this link.

ObservableCollectionEx should be used exactly as ObservableCollection. It could be instantiated and used in place of ObservableCollection, or it could be derived from it. No special treatment is required.

In order to postpone notifications, it is recommended to use the using() directive:

Due to the design of notification arguments, it is not possible to combine different operations together. For example, it is not possible to Add and Remove elements on the same delayed instance unless Dispose() has been called in between these calls. Calling Dispose() will fire previously collected events and reinitialize operation.

Performance

In general, both ObservableCollection and ObservableCollectionEx provide comparable performance. Testing has been done using an array of 10,000 unique objects. Both ObservableCollection and ObservableCollectionEx where initialized with this array to pre allocate storage so it is not affecting timing results. Application has been run about dozen times to let JIT to optimize the executable before the test results were collected.

The test consisted of 10,000 Add, Replace, and Remove operations. Timing has been collected using the Stopwatch class and presented in milliseconds.

The value on the left represents the number of milliseconds it took to complete the test (Add, Replace, and Remove). The value on the bottom specifies the number of notification subscribers (handlers added to the CollectionChanged event).

As you can see from the graph, the performance of the interface with disabled notifications does not depend on the subscribers. Due to several performance enhancements, ObservableCollectionEx performs slightly better than ObservableCollection regardless of the number of subscribers but it obviously loses the Disabled interface once there is more than one subscriber.

The performance of ObservableCollectionEx when notifications are delayed is different compared to the results described above. Since notification is called only once, it saves some time but it requires some extra processing to unwind saved notifications. Time spent on notifications for ObservableCollection and ObservableCollectionEx are described by the following equitation:

ObservableCollection: overhead = (n * a) + (n * b)

ObservableCollectionEx: overhead = a + c + (n * b)

Where a is a constant overhead required to execute notification, n is number of changed elements, b is the cost of redrawing each individual element, and c the overhead required to execute delayed notification.

The value on the left represents the time required to complete notifications. The value on the bottom specifies the number of changed elements.

In these equations, values a and c are constants so the performance depends only on two elements: b – the time required to redraw each individual element, and n – the number of notified elements. As you know from calculus, b controls how steep the raise of the graph is. So when the time required to redraw each element (b) increases, these two lines meet sooner. It means it takes less changed elements to see performance benefits.

First of all thanks for sharing your code. It is very helpful for me in a WPF-APP.But also there I get a little (big) problem after changing normal ObservableCollection with ObservableCollectionEx. I tried to isolate the problem. If you will run the short provided code you can see that something that is working with ObservableCollections not working with ObservableCollectionEx.

Hi,From a quick glance at your code it doesn't appear that you are firing a NotifyCollectionChangedAction.Reset in the NotifyCollectionChangedEventArgs when the Delayed notification instance gets disposed? If so how does an ItemsControl render the new range of items that were added into the child delayed notification instance? From my previous understanding of WPF ItemsControl needs a RESET notifcication action for data binding to work with the underlying collection. In fact MSDN documentation for RESET action appears to indicate that as well >>>"You should report Reset in cases where the number of individual Add / Remove / Replace actions necessary to properly report changes in a collection becomes excessive. For example, you should report Reset if a list was completely re-ordered based on some operation such as sorting."

If you are doing some other majic, hats off to your implementation. If not, I believe a reset action is a performance killer.-Rohit

The performance gain is variable and based on the number of the listeners attached to the collection (I've included a formula to calculate it). So in one scenario it could be 1% and in another it could be 30%.Anyway, thank you for your feedback.

Interesting design. I ran into an issue, though, as I wanted to hook the CollectionChanged event which is public in the original ObservableCollection, but protected now. Any reason I can no longer do this?

You could not do it because this event is used to implement notification delays. You could not bypass assessors. If you do it will break the behavior and invalidate design.Why would you want to do it anyway?

The elements in MyCollectionB are created from the elements of MyCollectionA. So whenever MyCollectionA changes, MyCollectionB should create/delete items based on the added/removed elements of MyCollectionA.

But how to do this without knowing when MyCollectionA is changed?

There have to be different item types in my case because of a more complex data structure.

Would it be possible to create new events and eventargs, which are fired, when batch operations are over? E.g. The eventargs could have a newItems/oldItems dictionary for each action. This way only one event is fired and performance should be the same.

I think his point was the article states you chose to inherit ObservableCollection, but in the source code, ObservableCollectionEx does NOT in derive from ObservableCollection. Which hehe, I wish it did.

This is a very good question. The idea to use extension is attractive. It would have allowed delaying notifications on any instance of ObservableCollection.

In order to create a new ObservableCollection "shell" which delays the notifications it should refer to the same List instance as the original instance of ObservableCollection. This List instance is not accessible through public members of the class. So to reach inside the original ObservableCollection and get the List instance I need either use reflection or inheritance. I chose inheritance as it is a lot faster and it also follows good practices better that alternative method.

I also needed a way to initialize an instance of "shell" ObservableCollection with that particular instance of the List.

Later I might write an extension version which uses reflection as an alternative to this approach if I could figure out how to make it to implement IDisposable.

Since I can't remove my previous message titled "My vote of #3," I'd like to say, publicly, that while I would appreciate, and have asked the author for, more documentation in the article on how to use the ObservableCollectionEx from C#, WinForms, as I have provided in a lengthy comment below[^] ...

This article is so excellent technically, that it really deserves a #5.

Pimples on angels don't bother me for too long

best, Bill

"Beauty is in the eye of the beholder, and it may be necessary from time to time to give a stupid or misinformed beholder a black eye." Miss Piggy"

In the book ".Net Framework Design Guidelines" Second Edition, from Microsoft Development Series, Addison Wesley Krzystof Cwalina and Brad Abrams:DO NOT use the "Ex" (or a similar) suffix for an identifier to distinguish it from an earlier version..."

I think suffix "Ex" doesn't mean anything, only Extension. But anybody will can do a new extension...

And, of course, since they say don't do it... That is such complete nonsense: naming conventions change and adapt all the time. The author's reason for naming makes sense to me but if you don't like it, change it for yourself. Talk about anal. Sheesh.

"If you think it's expensive to hire a professional to do the job, wait until you hire an amateur." Red Adair.nils illegitimus carborundum

However, queuing all of these notification events and firing them all at once, could overwhelm the UI, which may be what Lesheniuk is observing. Why not just fire one change notification when all is said and done?

I have a similar custom collection that derives from Collection. It terms of functionality, it is similar to yours in that is fires change notifications only after batch updates. To do this, itexposes a ProcessingState property that is checked in the the override for InsertItem. It also exposes an AddRange method that fires change notification only after all items in collection has been added. A little simpler but it does the trick.

I have a similar class BindableCollection (derived from ObservableCollection) which allows to disable the CollectionChanged notification.

I have also made various performance tests and recognized the key factor. The number of bindings to the hosted collection-item is appealing the ObservableCollection performance. It's maybe interesting to see the performance profit on such a collection in relation to the count of item-bindings.

The performance various also on different platforms WPF, Silverlight and OOB-Silverlight.

The first graph in the article shows exactly that. The numbers at the bottom are number of bindings to hosted collection. So these are results for series of tests with 1, 2, 4 and 8 bindings respectively.... and you right performance is slightly different on each of these platforms but not by much.

You are absolutrly right. Notifications do slow down redrawing considerably specially if all of elements needs to be redrawn after each notification. Can you share your code so everyone could see your results?