Friday, February 05, 2016

Modifying collections from different threads and still binding it via WPF

angry developer This post is discussing the solution to the NotSupportedException "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread" and also the InvalidOperationException "An ItemsControl is inconsistent with its items source".

In the first case you want to bind in Windows Presentation Foundation a collection property from your viewmodel and it says no. What happens is that you are using a BindingList<T> or an ObservableCollection<T> and the binding system is using in the background a CollectionView that wraps it which does not support changes via multiple threads. The solution to this is rather simple: use this piece of code:
BindingOperations.EnableCollectionSynchronization(collection, lockObject);
This short blog post from Florent Pellet explains things a little, but that is ending on a dire note: The ViewModel becomes dependent on the view It also suggests that you need to create a lock object for each UI thread, if you have more.

This works in .NET 4.5 and that is the reason that when you are looking the exception up you get all kind of answers that either suggest you invoke any changes on the Dispatcher UI thread (which I believe is against the idea of having a viewmodel) or weird bastardizations of the collection classes used, like trying to invoke the events for list changes on the dispatcher of the invoking delegate. I've tried that and I got the second InvalidOperationException exception that I will be covering later on :)

But let's go further and examine what is going on. If you look at the method declaration, EnableCollectionSynchronization also allows specifying a synchronization callback, something that you could use to manage weird custom collection classes. The Remarks sections says When you call this overload of the EnableCollectionSynchronization(IEnumerable, Object) method, the system locks the collection when you access it, which implies you are losing some performance, but not much else. In case you have many parallel threads that are modifying your collection, you need to lock it, anyway. You may, of course, create your own high performance system of changing a collection and, maybe, run a separate method to marshal changes from your private data structure to the UI bound one.

Now, the InvalidOperationException "An ItemsControl is inconsistent with its items source" is thrown when the ItemsSource property has become out of sync with the Items property, which is usually generated by the ItemsControl. So when I tried to create my own badass collection class, I managed to avoid the first exception and I got this one. The same solution applies to both cases:
BindingOperations.EnableCollectionSynchronization(collection, lockObject);
Funny enough, you have to run this piece of code on the Dispatcher UI thread.

But where to use it? It would be rather simple to use it in the viewodel constructor, using the ((ICollection)collection)SyncRoot object, or even in the constructor of a class that inherits from either BindingList or ObservableCollection and has nothing but this type of initialization. I believe that, since this is a binding issues, something within WPF, then the binding system should handle it, like some type of synchronizing Binding. For a second I thought that the IsAsync property of the Binding class would solve this by itself, but it wouldn't work. Also, Binding doesn't have any methods to override and BindingBase is an abstract class with internal methods to implement, which of course doesn't work. Otherwise it would have been OK, I believe, to create a special SynchronizedCollectionBinding class that enables collection synchronization at bind time. BTW, if you are thinking to implement everything starting with MarkupExtension, forget it. The Binding class is a bit hardcoded in Visual Studio and it wouldn't actually work as expected.

That's it, folks!