views:

208

answers:

3

When updating a collection of business objects on a background thread I get this error message:

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

Ok, that makes sense. But it also begs the question, what version of CollectionView does support multiple threads and how do I make my objects use it?

A: 

None of them, just use Dispatcher.BeginInvoke

Paul Betts
That defeats the purpose of having background threads and an independent data layer.
Jonathan Allen
No it doesn't - all of the work is to fetch the data / process it; you do this in the background thread, then use Dispatcher.BeginInvoke to move it to the collection (which takes very little time hopefully).
Paul Betts
A: 

Found one.

public class MTObservableCollection<T> : ObservableCollection<T>
{
   public override event NotifyCollectionChangedEventHandler CollectionChanged;
   protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   {
      var eh = CollectionChanged;
      if (eh != null)
      {
         Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
                 let dpo = nh.Target as DispatcherObject
                 where dpo != null
                 select dpo.Dispatcher).FirstOrDefault();

        if (dispatcher != null && dispatcher.CheckAccess() == false)
        {
           dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
        }
        else
        {
           foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
              nh.Invoke(this, e);
        }
     }
  }
}

http://www.julmar.com/blog/mark/2009/04/01/AddingToAnObservableCollectionFromABackgroundThread.aspx

Jonathan Allen
Note that this will cause a thread switch for each collection change and that all changes are serialized (which defeats the purpose of having background threads :-)). For a few items it doesn't matter but if you plan to add many items it will hurt performance a lot. I usually add the items to another collection in the background thread and then move them to the gui collection on a timer.
adrianm
I can live with that. The cost I'm trying to avoid is fetching the items in the first place, as it will lock the UI. Adding them to the collection is cheap by comparison.
Jonathan Allen
+1  A: 

This post by Bea Stollnitz explains that error message and why it's worded the way it is.

EDIT: From Bea's blog

Unfortunately, this code results in an exception: “NotSupportedException – This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.” I understand this error message leads people to think that, if the CollectionView they’re using doesn’t support cross-thread changes, then they have to find the one that does. Well, this error message is a little misleading: none of the CollectionViews we provide out of the box supports cross-thread collection changes. And no, unfortunately we can not fix the error message at this point, we are very much locked down.

Cameron MacFarland
I like mark's implementation better, but I have to give you the credit for finding the best explaination.
Jonathan Allen