3

There are several posts that explain the usage of BindingOperations.EnableCollectionSynchronization. E.g. BindingOperations.EnableCollectionSynchronization mystery in WPF or Using BindingOperations.EnableCollectionSynchronization

However, my understanding of a "lock" does not match the behavior of the following demonstration.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var itemsLock = new object();
    var items = new ObservableCollection<string>();
    BindingOperations.EnableCollectionSynchronization(items, itemsLock);

    Task.Run(() =>
    {
        lock (itemsLock)
        {
            Debug.WriteLine("task inside lock");
            Thread.Sleep(5000);
            items.Where(m => m == "foo").ToArray();
        }
        Debug.WriteLine("task outside lock");
    });

    Thread.Sleep(1000);
    Debug.WriteLine("UI thread add..");
    items.Add("foo");
    Debug.WriteLine("UI thread add..done");
}

Due to the lock, I expected Debug Output like this:

task inside lock
UI thread add..
task outside lock
UI thread add..done

But I find an Debug Output like this:

task inside lock
UI thread add..
UI thread add..done
task outside lock

Background info: I occasionally experience InvalidOperationExceptions "collection was modified" when running LINQ queries on a frequently changed ObservableCollection. This lead me to breaking it down to the previous sample. Then I found that my assumption of how EnableCollectionSynchronization works is wrong.

John Cummings
  • 1,949
  • 3
  • 22
  • 38
sa.he
  • 1,391
  • 12
  • 25

1 Answers1

6

You should synchronize all access to the collection using the same lock, i.e. you should lock around the call to Add:

lock (itemsLock)
    items.Add("foo");

The documentation is pretty clear on this:

To use a collection on multiple threads, one of which is the UI thread that owns the ItemsControl, an application has the following responsibilities:

  • Choose a synchronization mechanism.
  • Synchronize all access from the application to the collection using that mechanism.
  • Call EnableCollectionSynchronization to inform WPF of the mechanism.
  • ...
mm8
  • 163,881
  • 10
  • 57
  • 88
  • 3
    Shame on me - I must have been blind. Somehow I thought that BindingOperations.EnableCollectionSynchronization makes my ObservableCollection thread-safe. All it however does is sharing the lock object with the WPF Binding. – sa.he Jul 02 '19 at 15:25