UIDispatcher.Current not working as expected

Jul 7, 2011 at 11:17 AM

I expected UIDispatcher.Current to always return the one-and-only (singleton) dispatcher for the UI thread. But that seems not to be the case. When I call UIDispatcher.Current on an arbitrary thread (in my case: a callback thread from a Timer.Elapsed event handler), it creates a new Dispatcher instance for the current thread.

Is that a bug?

Coordinator
Jul 7, 2011 at 3:28 PM

No, it's not a bug. However, there is a difference in behavior between WPF and Silverlight. In WPF you have to initialize the dispatcher to UIDispatcher.Current while you are on the UI thread. If you are another thread you'll get a different dispatcher, as you've discovered, which won't do you any good.  You probably want to initialize the dispatcher in the ctor of your ViewModel, then call CheckAccess and BeginInvoke on that member variable when you are on the worker thread.

In Silverlight, on the other hand, UIDispatcher.Current always gives you the dispatcher for the UI thread, which I think is more practical.  After all, the reason why you're getting the dispatcher is to marshal a method call to the UI thread.

You need to keep in mind the difference between WPF and Silverlight when using the Dispatcher.  Thanks for bringing up this question.

Tony

Jul 7, 2011 at 4:07 PM

Hi Tony,

thanks for your immediate reply. My question was related to WPF as you already guessed.

By "initialize the dispatcher to UIDispatcher.Current" you mean I should access this property somehow from the UI thread during initialization? Probably like this:

UIDispatcher.Current.CheckAccess();

I cannot see how this could solve the problem. In WPF UIDispatcher.Current simply delegates to

System.Windows.Threading.Dispatcher.CurrentDispatcher;
Here is the source code I found for the Dispatcher class:

        /// <summary>
        ///     Returns the Dispatcher for the calling thread.
        /// </summary>
        /// <remarks> 
        ///     If there is no dispatcher available for the current thread,
        ///     and the thread allows a dispatcher to be auto-created, a new 
        ///     dispatcher will be created. 
        ///     <P/>
        ///     If there is no dispatcher for the current thread, and thread 
        ///     does not allow one to be auto-created, an exception is thrown.
        /// </remarks>
        public static Dispatcher CurrentDispatcher
        { 
            get
            { 
                // Find the dispatcher for this thread. 
                Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
 
                // Auto-create the dispatcher if there is no dispatcher for
                // this thread (if we are allowed to).
                if(currentDispatcher == null)
                { 
                    currentDispatcher = new Dispatcher();
                } 
 
                return currentDispatcher;
            } 
        }

I my case, this always creates a new Dispatcher for the non-UI thread.

Compare that to the implementation of the Execute class from the Calibrun Micro framework.

  /// <summary>
  /// Enables easy marshalling of code to the UI thread.
  /// </summary>
  public static class Execute
  {
    private static Action<Action> executor = action => action();

    /// <summary>
    /// Initializes the framework using the current dispatcher.
    /// </summary>
    public static void InitializeWithDispatcher()
    {
      var dispatcher = Dispatcher.CurrentDispatcher;
      executor = action =>
      {
        if (dispatcher.CheckAccess())
          action();
        else dispatcher.Invoke(action);
      };
    }

    /// <summary>
    /// Executes the action on the UI thread.
    /// </summary>
    /// <param name="action">The action to execute.</param>
    public static void OnUIThread(this System.Action action)
    {
      executor(action);
    }
  }

Here they capture(!) the dispatcher retrieved during initialization and use the captured(!) dispatcher inside the OnUIThread method.

I still believe there's something missing in the SimpleMvvmToolkit.

Coordinator
Jul 7, 2011 at 5:53 PM

Yes, you need to initialize a reference to the dispatcher while you are on the UI thread.  For example, you could do it in the ctor of a View or ViewMode. Then call CheckAccess and, if that returns false, call BeginInvoke.

public class MyView
{
  Dispatcher _dispatcher;
  public MyView
  {
    _dispatcher = UIDispatcher.Current;
  }

  public void DoSomethingOnAnotherThread()
  {
    Action method = () => myLabel.Content = "Foo";
    if (_dispatcher.CheckAccess()) method();
    else _dispatcher.BeginInvoke(method);
  }
}

I have modified the code in the next release of the toolkit to handle some of this internally.  For example, calling Notify or NotifyPropertyChanged in the ViewModel will marshal the call transparently.

Cheers,
Tony 

Jul 8, 2011 at 5:14 AM

Hi Tony,

ah, I see. I have to keep track of the Dispatcher reference returned by UIDispatcher.Current myself. I was thinking, the UIDispatcher class handles this internally for me, like the Execute class from Caliburn Micro does. You might want to consider this for the next release as it makes using the UIDispatcher at lot easier.

Thanks,
Chris 

Coordinator
Jul 8, 2011 at 1:12 PM
Edited Jul 8, 2011 at 1:17 PM

I haven't yet looked at Caliburn Micro. How does the Execute class get a reference to the Dispatcher from the UI thread?

What I was planning to do is grab a reference to the UI thread's dispatcher in the ctor of the ViewModelBase class.  That way, calls to Notify and NotifyPropertyChanged would correctly marshal to the UI thread for WPF apps.  (The marshaling already takes place correctly for Silverlight apps.)  But you would still need to grab the dispatcher reference yourself if you wanted to do something other than firing the notification or property changed events from a ViewModel.  Having UIDispatcher do this internally would be a nice thing for WPF apps.

Jul 8, 2011 at 1:20 PM

It's in the Execute class. I included the essential extract of this class in my second post on this discussion (see above). You have to call Execute.InitializeWithDispatcher() once (e. g. from App_Startup). Then you can simply call Execute.OnUIThread(() => YourCode) from any thread.

The dispatcher reference is stored ("captured") inside Action assigned to the static Execute.executor member.

Coordinator
Jul 8, 2011 at 1:34 PM

I see it now. :) I'll just add a static Initialize method to UIDispatcher that captures the dispatcher for WPF apps.  What the heck, I'll even throw in an Execute method that takes an Action.

Thanks for the feedback!

Cheers,

Tony