I like the MVVM pattern in WPF. No doubt, it’s
a perfect way to separate UI from logic. But you also get some other features out
of the box when you apply this pattern. One of my favorites is binding
collections to the UI and manipulating them with the use of a background
thread. If you’re doing it right, you get a responsive UI while performing
heavy background work without any additional effort required synchronizing the
data to the view. WPF is just doing it for you.
Ok. That’s not 100% of the truth and of course
it’s not that easy as it sounds. But there is not much to do. The data is
coming from a background thread and it should be displayed in the UI thread.
You know what that means? Correct. Someone has to dispatch the data to the UI
thread. Luckily there’s a class called Dispatcher in WPF. Even better, you can
use the Dispatcher anywhere in your WPF application because it provides a
static property (Current) that gives you access to the current Dispatcher
instance of the application. On the instance object you have the possibility to
invoke any method on the UI thread you like, either synchronous or asynchronous.
That was the preferred way until WPF 4.5. The problem was that it was not
working in every case. Have you ever tried to modify an ObersableCollection
from a background thread while it is bound to a ListView? The invoke methods on
the Dispatcher won’t help you. With WPF 4.5 there’s a better and more comfortable
approach to that problem: The EnableCollectionSynchronization method.
The EnableCollectionSynchronization method is a
static method on the BindingOperations class in the System.Windows.Data
namespace. The method has two overloads for controlling the access to the
underlying collection from several threads (just have a look at http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx).
So, with this method you may just write something like the following in your view
model
BindingOperations.EnableCollectionSynchronization(ListItems,
lockObject);
where ListItems is your observable collection
and lockObject is some object that will be used by the infrastructure to synchronize
the access to the collection from different threads. The magic happens behind
the scenes. The method is just a wrapper to some internal functionality
enabling the underlying CollectionView to allow cross thread changes. If you
want to know some more about WPF internals on handling views and collections
you’re invited to read the next parts of the article, otherwise, if you just
want to perform cross thread changes to collections bound to UI elements not
worrying about how it works, then all relevant information has just been said.
WPF View Management
While using the new EnableCollectionSynchronization
method I was wondering what happened behind the scenes when I use that method.
In order to cure my curiosity I had a look into the framework and its internal
classes. The key requirement of the WPF team was to allow the binding of one
collection to many views without having strong references. Additionally the
views should be able to sort, filter and navigate the collection without
affecting other views or even the source collection itself. Because of those
requirements the collection cannot manage its associated views and view
management has to happen outside of the collection view relationship. That’s
why there is a global ViewManager class that exists per application. This is an
internal class with a static Current property. The view manager is just a
dictionary that maps all bound collections to corresponding CollectionRecord
objects. The collection record is an
internal utility class that keeps a weak reference to a view table and also has
its own SynchronizationInfo. The ViewTable class is again a dictionary that
manages CollectionViewSources and ViewRecords. Again, the view record acts as a
utility class and encapsulates the access to the underlying CollectionView that
is used by WPF for realizing sorting, filtering, grouping and navigation functionalities
of collections on the UI. The collection view source is the XAML proxy of the
collection view. The following picture shows the relationships between the
mentioned classes.
When you call the EnableCollectionSynchronization
method, the call is handled by the RegisterCollectionSynchronizationCallback
method of the view manager, which in turn passes it to every collection view
that belongs to the given collection. The CollectionView class contains an
internal method SetAllowsCrossThreadChanges (see the following listing) which
just sets a flag in the collection view whether to allow or deny cross thread
operations on the underlying collection.
internal void SetAllowsCrossThreadChanges(bool value){
bool flag = this.CheckFlag(
CollectionView.CollectionViewFlags.AllowsCrossThreadChanges);
if (flag == value)
{
return;
}
this.SetFlag(
CollectionView.CollectionViewFlags.AllowsCrossThreadChanges, value);
this.OnAllowsCrossThreadChangesChanged();
}
This flag is evaluated on binding operations (in
our case changing the observable collection). The synchronization info on the
collection record is used to synchronize the access the collection. This is
where the two overloads of the EnableCollectionSynchronization come into play.
The first one takes a lock object that is now used for synchronization. The
second overload takes a callback that will be called during synchronization to
handle the access among several threads. This provides the possibility for the
client to manage the synchronization itself.