Have worker thread update ObservableCollection that is bound to a ListCollectionView

While playing around with WPF, I tried to do some multithreading where I have a worker thread updating my ObservableCollection, while having a ListCollectionView of that ObservableCollection being shown on a ListBox.

It was surprising to see that I get a NotSupportedException thrown, with the message saying 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'.  That doesn't seem to make sense - In my mind, I understand how the thread that created the UI should be the one that handles all UI updates.  However, the data itself should be able to reside anywhere, and I should be able to update it however and whenever I want.

Looking for a solution, I created a class deriving from ObservableCollection (the class name I chose is ObservableCollectionEx) thinking that I would just manually walk through the event's invocation list.  Well, that didn't quite work, since events are not accessible (other than for adding/removing delegates) to child classes.  Looking at the documentation, the CollectionChanged event in ObservableCollection is virtual - that means I can override it! Yeah!  I am glad someone at Microsoft decided to make that virtual.

So here's the code that I created - the pain is I now have to rename all occurrences of ObservableCollection to this new class.  Oh well, at least making it work with threads isn't too painful. 

public class ObservableCollectionEx: ObservableCollection { // Override the event so this class can access it public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;

protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { // Be nice - use BlockReentrancy like MSDN said using (BlockReentrancy) { System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged; if (eventHandler == null) return;

Delegate[] delegates = eventHandler.GetInvocationList; // Walk thru invocation list foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates) { DispatcherObject dispatcherObject = handler.Target as DispatcherObject; // If the subscriber is a DispatcherObject and different thread if (dispatcherObject!= null && dispatcherObject.CheckAccess == false) { // Invoke handler in the target dispatcher's thread dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e); } else // Execute handler as is handler(this, e); } } } }

 Update:

There is a bug with the code where subscribers in the same thread won't get called - the code above has been updated with the fix.

It also uses CheckAccess (available, but not shown via Intellisense as mentioned here).

This article is part of the GWB Archives. Original Author: Muljadi Budiman

New on Geeks with Blogs