13

I have been struggling to grasp this concept and even after many experiments I still can't figure out what the best practise is with ObservableCollections in WPF and using BindingOperations.EnableCollectionSynchronization.

If I have a viewmodel with an observable collection and I enable collection sync on it using a lock as shown below:

m_obsverableCollection = new ObservableCollection<..>;
BindingOperations.EnableCollectionSynchronization(m_obsverableCollection,
                                                   m_obsverableCollectionLock);

Does that mean that every modification and enumeration over that observable collection will:

  1. Lock the collection automatically using the m_obsverableCollectionLock?
  2. Marshall all modifications on the thread on which the collection was created?
  3. Marshall all modifications on the thread on which the binding operations call was made?

When using BindingOperations.EnableCollectionSynchronization, will I ever need to do any kind of locking explicitly?

The problem which spawned all this is that even after using BindingOperations.EnableCollectionSynchronization and locking items using the same lock I passed into that method, very occasionally I get the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread." exception

Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
Ruskin
  • 1,504
  • 13
  • 25
  • Can you post code that how you are trying to modify the collection and from where? I tried it in small app and it works for me perfectly. – Rohit Vats Nov 02 '13 at 13:20
  • The problem was intermittent so couldn't add code to reproduce it. The issue was that the BindingOperations.EnableSynchronization has to be done via the UI thread otherwise there is a potential of exceptions being thrown when the collection is accessed via non UI threads – Ruskin Jan 14 '14 at 09:44
  • Are you updating the ObservableCollection on the main UI thread or from the background thread? If from the bg thread, are you making the update using a call to the `Application.Current.Dispatcher.BeginInvoke()`? Have you tried this invocation using a simple `lock()` statement (~sanity)...? – code4life Dec 04 '14 at 04:32

2 Answers2

13

We finally got to the bottom of it:

We have to enable CollectionSynchronization on the dispatcher:

Application.Current.Dispatcher.BeginInvoke(new Action(()=>
{
    BindingOperations.EnableCollectionSynchronization(m_obsverableCollection, m_observableCollectionLock);
}));

Then everytime any other thread wants to access the observable you can simply:

lock (m_observableCollectionLock)
    m_observableCollection.Add(...)
Ruskin
  • 1,504
  • 13
  • 25
  • doing the same thing, but still getting exceptions such as "System.InvalidOperationException: 'Added item does not appear at given index '0'." – akay Apr 23 '21 at 10:14
2

I haven't used that particular syntax, but whenever I need to update an ObservableCollection from a background thread, I follow this pattern:

// i just typed this off the top of my head, so be forewarned...  :)
lock(someLockingObj)
{
    Application.Current.Dispatcher.BeginInvoke(new Action(()=>
    {
       ... update here....
    }));
}

Usually the error that you've encountered occurs when a bg thread is trying to update the ObservableCollection directly, without the Dispatcher.BeginInvoke (or even Invoke would work too, most of the times, IMHO).

code4life
  • 15,655
  • 7
  • 50
  • 82
  • Thanks for that, I think to avoid that they added the BindingOperations class, so we solved it using that...see answer below :) – Ruskin Dec 15 '14 at 07:53