Search
Close this search box.

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 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.

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: New Things I Learned

Related Posts