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.
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:
EnableCollectionSynchronization to inform WPF of the mechanism.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With