Wednesday, December 16, 2009

Dispatching in Silverlight

Anyone who has been building Silverlight and/or WPF applications for some time understands that there is a single UI thread that has special access requirements if you are going to be updating elements. In other words, if I spin off a new thread, I cannot arbitrarily impact the UI unless I get back to the UI thread using a dispatcher.

Fortunately, Silverlight makes it very easy to find your dispatcher, because it is a property on any UIElement that exists, including your own user control. If I need to make sure a thread executes on the UI thread, I simply do something like this:


Things get a little more tricky if you are trying to access the dispatcher from an entity that doesn't know about the UI thread ... for example, a view model.

Let's say you have a view model that needs to kick of a separate thread to do some work, and then update properties that are bound to the UI. By design, the view model interacts with services and exposes properties but it doesn't know about the view. We could sell out by giving it a Dispatcher property, but that could get ugly in scenarios such as unit tests where there simply may not be a UI thread.

Once again I'm happy to go back to the C# keywords that make our lives easier. In the past, I would have simply raised an event, "I need this" and passed some arguments, but with Action at our fingertips, it's so much easier!

In fact, this is all it really takes ... note the use of an action that takes a, um, ahem, action:

public class ViewModel 

   public Action<Action> UIAction { get; set; }

   public ViewModel()
      UIAction = ((uiAction)=>uiAction());


Seems fairly simple, doesn't it? Any time I am going to update one of my databound properties in a disconncted thread, I simply do this:

UIAction(()=>{ viewModel.Property = "New value";}); 

Right now, this isn't doing anything really interesting, because the action delegate simply calls the action we pass ... very boring, really. It will pass unit tests and all of that jazz ... so why'd I do it?

Because, once I bring the view model into an actual view, then I'll need to give it the dispatcher. Then, I can do something like this:

ViewModel viewModel = new ViewModel();
this.DataContext = viewModel; // or bind it with MEF to make it more fun
viewModel.UIAction = ((uiAction) => Dispatcher.BeginInvoke(()=>uiAction())); 

Now we've set it up so that when you call the action with an action, the action is to perform the action inside the dispatcher thread (got all that?). Now the view model plays equally well in the view and out of the view. It is still ignorant of what the view is or even that there is a dispatcher, and only needs to know that updating protected properties should be wrapped in the UIAction.

Let's save the argument over whether we should even be spinning threads from the view model that modify the UI thread for another day, mmmmm kay?

Jeremy Likness

1 comment:

  1. Well, the BackgroundWorker can do the same without adding dependency on Dispatcher. BW will raise notification of completion on same thread as it was created, and it will be UI thread in your case.