Using the Dispatcher with MVVM
When writing an MVVM application, you want to separate from the UI. However you also need to make sure that UI updates happen on the UI thread. Changes made through INotifyPropertyChanged get automatically marshaled to the UI thread, so in most cases you’ll be fine. However, when using INotifyCollectionChanged (such as with an ObservableCollection), these changes are not marshaled to the UI thread.
This means that unless you modify your collection on the UI thread, you’ll get a cross threading error. Thus the problem. We’re in the ViewModel and we don’t have access to the Dispatcher. That’s on a View object. How do we update the collection on the UI thread?
I came up with a small static class to help with this:
public static class DispatchService
{
public static void Invoke(Action action)
{
Dispatcher dispatchObject = Application.Current.Dispatcher;
if (dispatchObject == null || dispatchObject.CheckAccess())
{
action();
}
else
{
dispatchObject.Invoke(action);
}
}
}
When your ViewModel gives the DispatchService actions, it can run them on the correct thread. Which is, in the case of unit tests, always the current thread. A call to the DispatchService may look like this:
DispatchService.Invoke(() =>
{
this.MyCollection.Add("new value");
});
The use of a simple lambda statement makes it a relatively lightweight way of updating on the correct thread.
Comments
Anonymous
June 14, 2010
"In your main window constructor, set the DispatchObject to its Dispatcher." Can I do this in XAML? How? Thanks!Anonymous
June 25, 2010
Can I use this for any objects, not just Collections? Is that advisable?Anonymous
July 26, 2010
I don't think you can do this in XAML. And yes, you can use the dispatcher for anything you need to do. It's just most useful when dealing with collections because when settings properties it does the work on the correct thread automatically.Anonymous
March 07, 2011
Simply solution to an annoying problem, thanks alot!Anonymous
July 21, 2011
Why not just habitually derive your viewmodel from DispatcherObject and call VerifyAccess( ) in all your public methods and properties? Dispatcher is simply a threading model, it has no necessary correlation to GUI, and I'd wager people aren't clogging their viewmodels with locks. Or let's just say, I would be very disappointed if they were.Anonymous
February 18, 2012
Straight to the point! I added the code: DispatchService.DispatchObject = this.Dispatcher; in my App.Xaml.cs class constructor and then invoked DispatchService.Dispatch inside the BackgroundWorker.DoWork method, in order to have the UI updated as a consequence of the methods inside the DoWork code. Thanks!Anonymous
May 28, 2012
The comment has been removedAnonymous
May 29, 2012
Good point, Vitaliy. I had actually already updated this in my code; I just had not gotten around to updating the blog post. Done.Anonymous
December 10, 2013
I don't have App.xaml because my application is using window form. In one window I am loading xaml view. In this case where should I initialize DispatchObject.Anonymous
September 15, 2014
Can I totally avoid DispatcherService in my viewmodel if i don't want to update UI on collection changed event ? Instead I can use RaisePropertyChanged(()=>MyCollection) to update the UI once asynchronous operation is over. But it confuses me since i have seen application crashing while updating properties not collection from viewmodel which are data bound to UI. please elaborate the behaviorAnonymous
October 08, 2014
This also work fine for unit tests if I add check for Application.Current != null first. Final code become like this then. public static void Invoke(Action action) { if (Application.Current != null) { Dispatcher dispatchObject = Application.Current.Dispatcher; if (dispatchObject == null || dispatchObject.CheckAccess()) { action(); } else { dispatchObject.Invoke(action); } } else { action(); } }Anonymous
July 30, 2016
Thank you so much! This solved my problem!