Tuesday, July 27, 2010

Wpf Dispatcher Misuse

Unlike the Highlander there can be more than one Dispatcher.  Just because you invokeSystem.Windows.Threading.Dispatcher.CurrentDispatcher does not mean you will always get the Dispatcher responsible for executing tasks on the logical tree.

Consider this example:


        <TextBlock Text="{Binding Path=Field1}" />


namespace WpfDispatcherInfalibility
    using System;
    using System.ComponentModel;
    using System.Threading;
    using System.Windows.Threading;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : INotifyPropertyChanged 
        private string field1 = "Default";

        public MainWindow()
            this.DataContext = this;

            var thread = new Thread(this.WorkerMethod) { Name = "My worker thread 1" };

        public event PropertyChangedEventHandler PropertyChanged;

        public string Field1
                return this.field1;

                this.field1 = value;

        private void RaisePropertyChanged(string name)
            var handler = this.PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(name));

        private void WorkerMethod()
            // This will not retrieve the UI dispatcher because it is simply getting a dispatcher associated 
            // with this thread and this is not the UI thread. Just because you invoke Dispatcher.CurrentDispatcher
            // does not mean you will be given the dispatcher associated with the UI.
            var dispatcher = Dispatcher.CurrentDispatcher;
            string threadName = Thread.CurrentThread.Name;

            // The lambda will never be invoked because the dispatcher retrieved is not running.
            dispatcher.BeginInvoke(new Action(() => this.Field1 = "Field set from thread " + threadName));

As expected the UI shows the following window. And this window's text does not change.
What is the moral of the story?

Keep a reference to the dispatcher from a known point inside the controller for your xaml.  Or you can get it from any derivative of DispatcherObject (which is the root object of all Xaml components).

No comments:

Post a Comment