Skip to main content

...

....

C# WPF Tutorial - Working With The WPF Dispatcher [Intermediate]


Proper use of threads can greatly increase the responsiveness of your WPF applications. Unfortunately, you can't update any UI controls from a thread that doesn't own the control. In .NET 2.0, you used Control.Invoke(). Now, we've got something similar but more powerful - the Dispatcher. This tutorial will explain what the Dispatcher is and how to use it.

First of all, you need to know where the Dispatcher lives. Every Visual (Button, Textbox, Combobox, etc.) inherits from DispacterObject. This object is what allows you to get a hold of the UI thread's Dispatcher.

The Dispatcher is why you can't directly update controls from outside threads. Anytime you update a Visual, the work is sent through the Dispatcher to the UI thread. The control itself can only be touched by it's owning thread. If you try to do anything with a control from another thread, you'll get a runtime exception. Here's an example that demonstrates this:
public partial class Window1 : Window
{
  public Window1()
  {
    InitializeComponent();

    CheckBox myCheckBox = new CheckBox();
    myCheckBox.Content = "A Checkbox";

    System.Threading.Thread thread = new System.Threading.Thread(
      new System.Threading.ThreadStart(
        delegate()
        {
          myCheckBox.IsChecked = true;
        }
    ));

    thread.Start();
  }
}
 
This code creates a Checkbox, then creates a thread which tries to change the checked state. This will fail because the Checkbox was created on a different thread than the one trying to modify it. If you run this code, you'll end up with this exception:
The calling thread cannot access this object because a different thread owns it.

So the question is, how do you update the Checkbox from this thread? Fortunately, the Dispatcher gives us the ability to Invoke onto its thread. Invoking probably looks really familiar if you've programmed in .NET 2.0. We actually have an in-depth tutorial on invoking that you might want to read. Below is code using the Dispatcher that will run and update the Checkbox without throwing an exception.
public Window1()
{
  InitializeComponent();

  CheckBox myCheckBox = new CheckBox();
  myCheckBox.Content = "A Checkbox";

  System.Threading.Thread thread = new System.Threading.Thread(
    new System.Threading.ThreadStart(
      delegate()
      {
        myCheckBox.Dispatcher.Invoke(
          System.Windows.Threading.DispatcherPriority.Normal,
          new Action(
            delegate()
            {
              myCheckBox.IsChecked = true;
            }
        ));
      }
  ));

  thread.Start();
}
 
Now we've introduced the Dispatcher object. The call to Invoke needs to take a few pieces of information. First is the priority you'd like your work executed with. Next is the delegate that contains the work you actually want to do. If your delegate takes parameters, the Invoke call will also accept an Object or Object[] to pass into the delegate function. It will also accept a timespan that limits the amount of time the Invoke call will wait to execute your code.

The call to Invoke will block until your function has been executed. Depending on the priority you've set, this might take a while. The Dispatcher also has the ability to invoke code asynchronously using BeginInvoke. Let's look at the same example using BeginInvoke.
public Window1()
{
  InitializeComponent();

  CheckBox myCheckBox = new CheckBox();
  myCheckBox.Content = "A Checkbox";

  System.Threading.Thread thread = new System.Threading.Thread(
    new System.Threading.ThreadStart(
      delegate()
      {
        System.Windows.Threading.DispatcherOperation
          dispatcherOp = myCheckBox.Dispatcher.BeginInvoke(
          System.Windows.Threading.DispatcherPriority.Normal,
          new Action(
            delegate()
            {
              myCheckBox.IsChecked = true;
            }
        ));

        dispatcherOp.Completed += new EventHandler(dispatcherOp_Completed);
      }
  ));

  thread.Start();
}

void dispatcherOp_Completed(object sender, EventArgs e)
{
  Console.WriteLine("The checkbox has finished being updated!");
}
 
BeginInvoke takes many of the same arguments as Invoke, but now returns a DispatcherOperation that lets you keep track of the progress of your function. In this case, I simply hooked the Completed event that notifies me when the work has been completed. The DispatcherOperation object also lets you control the Dispatcher by changing its priority or aborting it all together.

As I mentioned above, we can limit the amount of time an Invoke is allowed to take my passing the Invoke call a TimeSpan structure. Here's an example that will wait 1 second for the queued function to complete:
public Window1()
{
  InitializeComponent();

  CheckBox myCheckBox = new CheckBox();
  myCheckBox.Content = "A Checkbox";

  System.Threading.Thread thread = new System.Threading.Thread(
    new System.Threading.ThreadStart(
      delegate()
      {
        myCheckBox.Dispatcher.Invoke(
          System.Windows.Threading.DispatcherPriority.SystemIdle,
          TimeSpan.FromSeconds(1),
          new Action(
            delegate()
            {
              myCheckBox.IsChecked = true;
            }
        ));
      }
  ));

  thread.Start();
}
 
Unfortunately, there's no way to determine if the invoke finished or timed out from the outside. You can always put code inside the invoked function to determine if it executed.

All right, so we've got our dispatchers working and code is being executed where it's supposed to be. Invokes, however, are kind of an expensive process. What happens when your controls are being updated from both external threads and the main UI thread. How do you know if you're supposed to use Invoke? The Dispatcher object provides a function that tells you whether or not you have to use Invoke. In WinForms you call InvokeRequired on the actual control. In WPF, you call CheckAccess() on the Dispatcher object. CheckAccess() returns a boolean indicating whether or not you can modify the control without Invoking.
if (!myCheckBox.Dispatcher.CheckAccess())
{
  myCheckBox.Dispatcher.Invoke(
    System.Windows.Threading.DispatcherPriority.Normal,
    new Action(
      delegate()
      {
        myCheckBox.IsChecked = true;
      }
  ));
}
else
{
  myCheckBox.IsChecked = true;
}
 
So now, before I invoke, I check to see if I even need to. If I do, I invoke the function, if I don't, I simply update the control directly.

As you can see, the Dispatcher provides a great deal of flexibility over the WinForms Invoke. There's a lot about WPF's dispatching model that we didn't touch in this tutorial. The System.Windows.Threading namespace contains a lot more useful objects that extends the power of the dispatcher even further.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as

C# WPF Tutorial - Implementing IScrollInfo [Advanced]

The ScrollViewer in WPF is pretty handy (and quite flexible) - especially when compared to what you had to work with in WinForms ( ScrollableControl ). 98% of the time, I can make the ScrollViewer do what I need it to for the given situation. Those other 2 percent, though, can get kind of hairy. Fortunately, WPF provides the IScrollInfo interface - which is what we will be talking about today. So what is IScrollInfo ? Well, it is a way to take over the logic behind scrolling, while still maintaining the look and feel of the standard ScrollViewer . Now, first off, why in the world would we want to do that? To answer that question, I'm going to take a an example from a tutorial that is over a year old now - Creating a Custom Panel Control . In that tutorial, we created our own custom WPF panel (that animated!). One of the issues with that panel though (and the WPF WrapPanel in general) is that you have to disable the horizontal scrollbar if you put the panel in a ScrollV