Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I avoid using the Dispatcher?

I've read many thread posts, articles etc. about binding and the thread affinity of GUI controls. There are some post in which the people don't want to use the Dispatcher.

I also have a workmate who avoids using the Dispatcher in his code. I asked him for the reason but his answer didn't satisfy me. He said, he don't like such kind of "magic" hidden in a class.

Well I'm a fan of the following class.


public class BindingBase : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;

   private Dispatcher Dispatcher
   {
#if SILVERLIGHT
      get { return Deployment.Current.Dispatcher; }
#else
      get { return Application.Current.Dispatcher; }
#endif
   }

   protected void RaisePropertyChanged<T>(Expression<Func<T>> expr)
   {
      var memberExpr = (MemberExpression)expr.Body;
      string property = memberExpr.Member.Name;

      var propertyChanged = PropertyChanged;
      if (propertyChanged == null) return;

      if (Dispatcher.CheckAccess())
         propertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
      else
         Dispatcher.BeginInvoke(() => RaisePropertyChanged(expr));
   }
}

Here is the question. Are there any reasons why some people don't want to use such a class? Perhaps I have to reconsider this approach.

You have to admit, there is one strange thing. Dispatcher.CheckAccess() is excluded from Intellisense. Perhaps they are a bit scary due to this fact.

Regards

EDIT:

Ok, another example. Consider a complex object. The collection as example was perhaps not the best idea.


public class ExampleVm : BindingBase
{
   private BigFatObject _someData;
   public BigFatObject SomeData
   {
      get { return _someData; }
      set
      {
         _someData = value;
         RaisePropertyChanged(() => SomeData);
      }
   }

   public ExampleVm()
   {
      new Action(LoadSomeData).BeginInvoke(null, null); //I know - it's quick and dirty
   }

   private void LoadSomeData()
   {
      // loading some data from somewhere ...
      // result is of type BigFatObject

      SomeData = result; // This would not work without the Dispatcher, would it?
   }
}
like image 734
DHN Avatar asked Mar 07 '11 08:03

DHN


1 Answers

I am also, personally, not against of Dispatcher in view model classes. I haven't seen any significant problems with it, yet it gives the most flexibility to your code.

But I like the idea of encapsulating the usage of Dispatcher in the infrastructure code as much as possible. Just like you did with RaisePropertyChanged method (BTW, in case of RaisePropertyChanged you don't have to dispatch anything - binding already does it for you; you only have to dispatch changes to collections).

The biggest and the only disadvantage that I see here is unit testing. Things can get tricky when you try to test your logic that involves usage of Dispatcher. Imagine if you had code something like this in a view model:

private void UpdateMyCollection() 
{
   IList<ModelData> dataItems = DataService.GetItems();

   // Update data on UI
   Dispatcher.BeginInvoke(new Action(() => {
      foreach (ModelData dataItem in dataItems)
      {
         MyObservableCollection.Add(new DataItemViewModel(dataItem));
      }
   }));
}

This kind of code is quite typical when it comes to updating collections from a non-UI thread. Now, how would you write a unit test that tests the logic of adding items to the observable collection? First of all, you will need to mock the Dispatcher property because Application.Current is null during unit test execution. Second of all, how will you mock it? Will you create a special thread that will mimic a UI thread and use the Dispatcher of that thread? So, this kind of things.

The bottom line is that if you want your code to be unit-test-friendly, you need to think of the way how you will mock the Dispatcher. This is the only concern.

Update:

The second example you provided WILL work without Dispatcher (binding will do the trick).

like image 86
Pavlo Glazkov Avatar answered Sep 28 '22 14:09

Pavlo Glazkov