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.

12 Replies to “Using the Dispatcher with MVVM”

  1. "In your main window constructor, set the DispatchObject to its Dispatcher."

    Can I do this in XAML? How?

    Thanks!

  2. 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.

  3. 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.

  4. 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!

  5. Why wouldn't you use

    System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => { MyObservableCollectionList.Add("xxx"); }));

    Using this line you don't need to keep reference to GUI dispatcher in your DispatchService.

  6. Good point, Vitaliy. I had actually already updated this in my code; I just had not gotten around to updating the blog post. Done.

  7. 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.

  8. 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 behavior

  9. 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();

               }

           }

Leave a Reply

Your email address will not be published. Required fields are marked *