Tuesday, May 26, 2009

Responsive WPF User Interfaces Part 6

Unresponsive Controls

Sorry about the unreasonable delay between this post and the last post in this series. Interestingly readers that have been following the series have already jumped ahead to see that not all is fine even if you follow the guidance provided. 'Arne' makes a comment on the last post that he is having some problems even when he follows the guidance especially when loading a lot of images. Some of the team I work with are currently working with graphing/charting products that are great with tens and hundreds of rows of data but create an unresponsive UI when pushed to use thousands of rows of data (eg +5 years of daily stock info).

So if we are following the rules what can we do? Well let us look at a concrete problem first and for consistency let's stay with the PhotoAlbum project. For a quick refresher, we have a "ViewModel" with a property Images that is of type ObservableCollection<Uri>. The XAML snippet that displays the Images looks like this:

<ListBox ItemsSource="{Binding Images}"
         ScrollViewer.HorizontalScrollBarVisibility="Disabled">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Border Background="AliceBlue"
              CornerRadius="2"
              Width="150"
              Height="150">
        <Image Source="{Binding}"
               MaxHeight="148"
               MaxWidth="148"
               Stretch="Uniform"
               StretchDirection="DownOnly"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"
               ToolTip="{Binding}" />
      </Border>
    </DataTemplate>
  </ListBox.ItemTemplate>
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel IsItemsHost="True" />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</ListBox>

Now in theory there is nothing wrong with this XAML. From a declarative programming point of view, I think it is well written (Ed: Cause I wrote it!) as it describes what we want (Items in the ListBox to be wrapped horizontally, displayed as Images within a border.

The problem with this declarative code is what most people fear with declarative code: what is actually happening under the hood? Before we trek too far, if I run the code (see example 6_UnresponsiveImages in the code example) I can select folders with small amount of small images just fine. The application runs ok. The current code becomes a problem when I point it to folders with large images. This is compounded when there are lots of those large images or when I am running on a slower/older computer.

We will quickly go off track to consider the likelihood of the large image slow computer problem. Digital cameras currently are getting cheaper and the quality is going up. Mid range cameras range between 8-12 megapixel and can have in excess of 8GB of memory.  Phones now are coming out with 5-8 megapixel cameras. If you come back and read this 12months from now, I guess you will laugh at the small sizes I refer to. Next consider the average user's computer. Rightly or wrongly, the release of vista saw very few new features that users felt they needed. Many people still run Win XP and do so on old hardware. Pentium 4 chips are still pervasive in many homes and offices. Loading a gig of 10 megapixel images in our current app does prove to be a performance problem.

We actually have 2 problems here:

  1. loading of the image in it's full resolution
  2. loading the image on the UI thread

We will only discuss issue 2 because technically this series is about responsive UIs, not performance (similar but not the same problem). So to get to the bottom of why our UI is laggy even though we have

  • loaded our Images on a different thread,
  • updated the UI using the dispatcher
  • and written nice XAML

we should understand a little bit about the Image control. The Image control has a Source property. Most often this source property is given a string or Uri, however some may have noticed that it actually is of type ImageSource. Through the magic of TypeConverters our strings and URI are converted to an appropriate sub class of ImageSource, most likely BitmapFrame. From having a quick browse over the code implemented around the ImageSource, BitmapSource, BitmapFrame & BitmapDecoder, I can only deduct that the only code executed off the UI thread is the downloading of the image where the source is a URI with http or https protocol. One may then argue that "well if it came from the file system wouldn't that be so fast that it could be done on the UI thread". One may argue that, but that is not the whole argument. Once the file has been read from disk it still must be decoded and then resized. While this generally is fast and would normally take less than 1 second, we should remember back to some magic number I made up that was 250ms-330ms (1/4 to 1/3 of a second) is the max the UI thread can hang for before it is very clear to the user. Now if I load just 8 images that each take 330ms-500ms to load from disk, decode and then resize this will create an awful user experience for the ender user as the progress bar stutters and images appear and the mouse is jerky.

To reproduce the problems discussed in this post you will want to get some hi-res images and load up the example code that can be found here. Try out the 6th example and point the source folder of the pop up widow to your folder with the hi-res images.

Next post we will look at the issues we will face with solving the problem of Images that kill the responsiveness of our UI. We will discuss fun things like decoders, freezables and maybe the parallel extensions that will become part of .NET 4.0

Previous - Responsive WPF User Interfaces Part 5 - Testing multi-threaded WPF code

Next - Responsive WPF User Interfaces Part 7 - Responsive Controls

Back to series Table Of Contents

Working version of the code can be found here

Monday, February 16, 2009

Responsive WPF User Interfaces Part 5

Testing multi-threaded WPF code

Stop press: Dont use this method. Read the article, but follow comment at bottom of post (ie Use Rx instead)

In the last post in this series we took a rather long way to get to the multi-threaded result we were looking for. This was because we decided to separate out our code in cohesive parts that allowed for more maintainable code. The popular method of creating maintainable code currently is through Unit Testing. An even better approach would be to apply Test Driven Development. TDD is a style of design where tests outlining our goals are written first and then code to satisfy those tests is created. Having identified the technical challenges of keeping a WPF application responsive, namely; the concepts related to the Dispatcher and parallel programming, we can now step back again to see the bigger picture as we did in part 4, to move forward.

In this post we will drive out our presentation model through tests and discover the issues related to testing WPF code and in particular testing the multi-threaded code. Just to set expectations, there is an assumption here that the reader is familiar with WPF Data Templates and unit testing with a mocking framework.

From my experience with unit testing it is always good to start off with a set of requirements that we could turn into tests. Continuing with our example of a thumbnail viewer, lets create a list of our requirements:

  • User can choose a folder
  • Can see what folder was selected
  • Will list all images in that directory and sub directories
  • Will indicate when it is processing
  • Cannot set folder while processing
  • The user interface will remain responsive during processing

For brevity, I will try to cheat as much as I can so more time can be spent on the technical issues of testing multi-threaded WPF code and less time spouting TDD goodness. I will first cheat by making the assumption that this is WPF code so will need implement constructs such as ICommand and INotifyPropertyChanged.

From these requirements I eventually came to the following test cases:

[TestClass]
class PhotoAlbumModelFixture
{
    [TestMethod]
    public void SetSourceCommand_sets_SourcePath(){}
    
    [TestMethod]
    public void SetSourceCommand_calls_FileService_with_value_from_FolderSelector(){}

    [TestMethod] 
    public void SetSourceCommand_calls_FileService_with_an_ImageOnly_filter() {}

    [TestMethod]
    public void SetSourceCommand_populates_images_from_FileService(){}

    [TestMethod]
    public void SetSourceCommand_recursively_calls_FileService(){}

    [TestMethod]
    public void SetSourceCommand_does_not_call_FileService_with_empty_value(){}

    [TestMethod]
    public void SetSourceCommand_sets_IsLoading_to_True(){}

    [TestMethod]
    public void SetSourceCommand_is_disabled_when_IsLoading(){}

    [TestMethod]
    public void FileService_is_called_Async(){}
}

When writing some of the tests I found that standard unit test code was fine. When I started to implement my code that had to make multithreaded calls, however, testing became problematic.

For example, my first attempt to write the "SetSourceCommand_calls_FileService_with_value_from_FolderSelector" test looked like this:

[TestMethod]
public void SetSourceCommand_calls_FileService_with_value_from_FolderSelector()
{
    Rhino.Mocks.MockRepository mockery = new Rhino.Mocks.MockRepository();
    ArtemisWest.Demo.ResponsiveUI._4_MultiThreaded.IFileService fileService = mockery.DynamicMock<ArtemisWest.Demo.ResponsiveUI._4_MultiThreaded.IFileService>();
    ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector folderSelector = mockery.DynamicMock<ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector>();

    PhotoAlbumModel model = new PhotoAlbumModel(Dispatcher.CurrentDispatcher, fileService, folderSelector);

    using (mockery.Record())
    {
        Expect.Call(folderSelector.SelectFolder()).Return(FolderPath);
        Expect.Call(fileService.ListFiles(FolderPath, null)).Return(EmptyList).IgnoreArguments().Constraints(
          Rhino.Mocks.Constraints.Is.Equal(FolderPath),
          Rhino.Mocks.Constraints.Is.Anything());
        Expect.Call(fileService.ListDirectories(FolderPath)).Return(EmptyList);
    }

    using (mockery.Playback())
    {
        model.SetSourcePathCommand.Execute(null);
    }
}

And following TDD rules of just write enough code to satisfy the test, my implementation of the Execute command handler looks like this:

void ExecuteSetSourcePath(object sender, ExecutedEventArgs e)
{
    SourcePath = this.folderSelector.SelectFolder();
    IsLoading = true;
    LoadPath(SourcePath);
    IsLoading = false;
}

void LoadPath(string path)
{
    IEnumerable<string> files = fileService.ListFiles(path, IsImage);
    foreach (string item in files)
    {
        Images.Add(item);
    }

    IEnumerable<string> directories = fileService.ListDirectories(path);
    foreach (string item in directories)
    {
        LoadPath(item);
    }
}

Astute readers will notice the complete lack of "Responsive UI Code". Well I have yet to reach my test that specifies that the call to FileService should be asynchronous. So after I have finished writing all of my other tests and the code to support them, I write some test code to enforce the responsive requirement:

[TestMethod]
public void FileService_is_called_Async()
{
    SlowFileServiceFake fileService = new SlowFileServiceFake();
    Rhino.Mocks.MockRepository mockery = new Rhino.Mocks.MockRepository();
    ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector folderSelector = mockery.DynamicMock<ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector>();

    PhotoAlbumModel model = new PhotoAlbumModel(Dispatcher.CurrentDispatcher, fileService, folderSelector);

    using (mockery.Record())
    {
        Expect.Call(folderSelector.SelectFolder()).Return(FolderPath);
    }

    using (mockery.Playback())
    {
        model.SetSourcePathCommand.Execute(null);
        //Due to the sleeps in the stub this should only have 1 item at this stage.
        Assert.IsTrue(model.Images.Count < fileService.ExpectedFiles.Count);
    }
    Thread.Sleep(300);
    CollectionAssert.AreEquivalent(fileService.ExpectedFiles, model.Images);
}

private class SlowFileServiceFake : ArtemisWest.Demo.ResponsiveUI._4_MultiThreaded.IFileService
{
    public readonly List<string> ExpectedFiles = new List<string>() { "value0", "value1", "value2", "value3", "value4", "value5" };

    public IEnumerable<string> ListFiles(string path, Predicate<string> filter)
    {
        foreach (var item in ExpectedFiles)
        {
            yield return item;
            Thread.Sleep(30);
        }
    }

    public IEnumerable<string> ListDirectories(string path)
    {
        return EmptyList;
    }
}

Here I have used a Fake to provide canned results with a hard coded delay. In theory this should drip feed values to the presentation model.

I now go back to update my model to get the tests to pass. Following my own advice I change the code to look like the following:

void ExecuteSetSourcePath(object sender, ExecutedEventArgs e)
{
    string folder = this.folderSelector.SelectFolder();
    if (!string.IsNullOrEmpty(folder))
    {
        SourcePath = folder;
        StartLoadingFiles(SourcePath);
    }
}

void StartLoadingFiles(string path)
{
    BackgroundWorker fileWorker = new BackgroundWorker();
    fileWorker.DoWork += (sender, e) =>
    {
        LoadPath(path);
    };
    fileWorker.RunWorkerCompleted += (sender, e) =>
    {
        IsLoading = false;
    };
    fileWorker.RunWorkerAsync();
}

void LoadPath(string path)
{
    IEnumerable<string> files = fileService.ListFiles(path, IsImage);
    foreach (string item in files)
    {
        dispatcher.Invoke(new Action(() => { Images.Add(item); }));
    }

    IEnumerable<string> directories = fileService.ListDirectories(path);
    foreach (string item in directories)
    {
        LoadPath(item);
    }
}

To my surprise this code does not satisfy the test! The reason is because the Dispatcher is not in an Executing loop. This would normally be started by WPF when the application starts, but here we are just in test code. The solution lies with the DispatcherFrame. A dispatcher frame represents an execution loop in a Dispatcher. We can kick start this loop by simply instantiating a new DispatcherFrame and calling Dispatcher.PushFrame with it as the parameter. See Dan Crevier's blog for a really simple way to unit test in this fashion. If we implement Dan's method we end up with the following test code:

[TestMethod]
public void FileService_is_called_Async()
{
    SlowFileServiceFake fileService = new SlowFileServiceFake();
    Rhino.Mocks.MockRepository mockery = new Rhino.Mocks.MockRepository();
    ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector folderSelector = mockery.DynamicMock<ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector>();

    PhotoAlbumModel model = new PhotoAlbumModel(Dispatcher.CurrentDispatcher, fileService, folderSelector);

    using (mockery.Record())
    {
        Expect.Call(folderSelector.SelectFolder()).Return(FolderPath);
    }

    DispatcherFrame frame = new DispatcherFrame();
    model.PropertyChanged += (sender, e) => 
    {
        if (e.PropertyName == "IsLoading" && !model.IsLoading)
        {
            frame.Continue = false;
        }
    };

    using (mockery.Playback())
    {
        model.SetSourcePathCommand.Execute(null);
        //Due to the sleeps in the stub this should only have 1 item at this stage.
        Assert.IsTrue(model.Images.Count < fileService.ExpectedFiles.Count);
        Dispatcher.PushFrame(frame);
    }
    Thread.Sleep(300);
    CollectionAssert.AreEquivalent(fileService.ExpectedFiles, model.Images);
}

We could call it a day here, however I am not happy enough with this style of coding. My first problem is the explicit sleep I have in there. This makes my test slow. The fastest this test will ever run is 300ms. I could replace that with some sort of loop to check progress but then my test code is looking less like test code and more like low level code to dance-around dispatchers. This raises my other issue; the clutter I have added by adding the DispatcherFrame stuff.

My solution here was to create a construct that allowed me to code my intentions in a way that I thought to be clearer. I wanted to be able to

  • test code that would involve multi-threaded code and the dispatcher
  • specify what condition defined its completion
  • specify a timeout
  • treat the code block as a blocking call

The result I came up with is the following:

[TestMethod]
public void FileService_is_called_Async()
{
    Rhino.Mocks.MockRepository mockery = new Rhino.Mocks.MockRepository();
    SlowFileServiceFake fileService = new SlowFileServiceFake();
    ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector folderSelector = mockery.DynamicMock<ArtemisWest.Demo.ResponsiveUI._5_MultiThreadedTest.IFolderSelector>();

    PhotoAlbumModel model = new PhotoAlbumModel(Dispatcher.CurrentDispatcher, fileService, folderSelector);
    using (mockery.Record())
    {
        Expect.Call(folderSelector.SelectFolder()).Return(FolderPath);
    }

    using (DispatcherTester dispatcherTester = new DispatcherTester(Dispatcher.CurrentDispatcher))
    {
        model.PropertyChanged += (sender, e) =>
        {
            if (e.PropertyName == "IsLoading" && !model.IsLoading)
                dispatcherTester.Complete();
        };
        dispatcherTester.Execute(() =>
          {
              model.SetSourcePathCommand.Execute(null);
              Assert.IsTrue(model.Images.Count < fileService.ExpectedFiles.Count);
          },
          new TimeSpan(0, 0, 2));
    }
    CollectionAssert.AreEquivalent(fileService.ExpectedFiles, model.Images);
}

This code describes a block of code that may require the use of the dispatcher, the condition where the asynchronous code is considered complete and a TimeSpan for a timeout. The test code is still not perfect but I think it is a step in the right direction.

The code for the DispatcherTester looks something like this:

internal sealed class DispatcherTester : IDisposable
{
  private readonly Dispatcher dispatcher;
  private readonly DispatcherFrame dispatcherFrame = new DispatcherFrame();

  public DispatcherTester(Dispatcher dispatcher)
  {
    this.dispatcher = dispatcher;
  }

  public Dispatcher Dispatcher
  {
    get { return dispatcher; }
  }

  public void Execute(Action action, TimeSpan timeout)
  {
    Execute(action, timeout, new TimeSpan(10));
  }

  public void Execute(Action action, TimeSpan timeout, TimeSpan wait)
  {
    Stopwatch stopwatch = Stopwatch.StartNew();
    action.Invoke();
    Dispatcher.PushFrame(dispatcherFrame);

    while (dispatcherFrame.Continue && stopwatch.Elapsed < timeout)
    {
      Thread.Sleep(wait);
    }
    if (stopwatch.Elapsed >= timeout)
    {
      dispatcherFrame.Continue = false;
      Dispatcher.DisableProcessing();
      Dispatcher.ExitAllFrames();
      throw new TimeoutException();
    }
  }

  public void Complete()
  {
    dispatcherFrame.Continue = false;
  }

  #region IDisposable Members

  public void Dispose()
  {
    dispatcherFrame.Continue = false;
  }

  #endregion
}

If any other developers have hit problems trying to write unit tests for their WPF code then I hope that this snippet of code helps. For those that are interested, the end result for the Model looks like

public class PhotoAlbumModel : INotifyPropertyChanged
{
  #region Fields
  private readonly Dispatcher dispatcher;
  private readonly _4_MultiThreaded.IFileService fileService;
  private readonly IFolderSelector folderSelector;
  private readonly DelegateCommand setSourcePathCommand;
  private readonly ObservableCollection<string> images = new ObservableCollection<string>();
  private string sourcePath;
  private bool isLoading = false;
  #endregion

  public PhotoAlbumModel(Dispatcher dispatcher, _4_MultiThreaded.IFileService fileService, IFolderSelector folderSelector)
  {
    this.dispatcher = dispatcher;
    this.fileService = fileService;
    this.folderSelector = folderSelector;
    setSourcePathCommand = new DelegateCommand(ExecuteSetSourcePath, CanSetSourcePath);
    this.PropertyChanged += (sender, e) => { if (e.PropertyName == "IsLoading") setSourcePathCommand.OnCanExecuteChanged(); };
  }

  public ObservableCollection<string> Images
  {
    get { return images; }
  }

  public bool IsLoading
  {
    get { return isLoading; }
    private set
    {
      isLoading = value;
      OnPropertyChanged("IsLoading");
    }
  }

  public string SourcePath
  {
    get { return sourcePath; }
    private set
    {
      sourcePath = value;
      OnPropertyChanged("SourcePath");
    }
  }

  public ICommand SetSourcePathCommand { get { return setSourcePathCommand; } }

  #region Command handlers
  void CanSetSourcePath(object sender, CanExecuteEventArgs e)
  {
    e.CanExecute = !IsLoading;
  }

  void ExecuteSetSourcePath(object sender, ExecutedEventArgs e)
  {
    string folder = this.folderSelector.SelectFolder();
    if (!string.IsNullOrEmpty(folder))
    {
      SourcePath = folder;
      StartLoadingFiles(SourcePath);
    }
  }
  #endregion

  #region Private method
  void StartLoadingFiles(string path)
  {
    IsLoading = true;
    BackgroundWorker fileWorker = new BackgroundWorker();
    fileWorker.DoWork += (sender, e) =>
    {
      LoadPath(path);
    };
    fileWorker.RunWorkerCompleted += (sender, e) =>
    {
      IsLoading = false;
    };
    fileWorker.RunWorkerAsync();
  }

  void LoadPath(string path)
  {
    IEnumerable<string> files = fileService.ListFiles(path, IsImage);
    foreach (string item in files)
    {
      dispatcher.Invoke(new Action(() => { Images.Add(item); }));
    }

    IEnumerable<string> directories = fileService.ListDirectories(path);
    foreach (string item in directories)
    {
      LoadPath(item);
    }
  }

  bool IsImage(string file)
  {
    string extension = file.ToLower().Substring(file.Length - 4);
    switch (extension)
    {
      case ".bmp":
      case ".gif":
      case ".jpg":
      case ".png":
        return true;
      default:
        return false;
    }
  }
  #endregion

  #region INotifyPropertyChanged Members
  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged(string propertyName)
  {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
      handler(this, new PropertyChangedEventArgs(propertyName));
    }
  }
  #endregion
}

Notice that the Folder dialogue code from the previous post has now been pushed out to an interface which cleans up the code and also allows us to test this model.

The data template looks almost identical to the previous post's window xaml:

<DataTemplate DataType="{x:Type local:PhotoAlbumModel}">
  <DataTemplate.Resources>
    <BooleanToVisibilityConverter x:Key="boolToVisConverter"/>
  </DataTemplate.Resources>
  <DockPanel>
    <DockPanel DockPanel.Dock="Top">
      <ProgressBar IsIndeterminate="True" Height="18" 
                   DockPanel.Dock="Top" 
                   Visibility="{Binding Path=IsLoading, 
                                Converter={StaticResource boolToVisConverter}}" />
      <Button x:Name="SetSourcePath" Command="{Binding Path=SetSourcePathCommand}" 
              DockPanel.Dock="Right">Set Source</Button>
      <Border BorderThickness="1" BorderBrush="LightBlue" Margin="3">
        <Grid>
          <TextBlock Text="{Binding SourcePath}">
            <TextBlock.Style>
              <Style TargetType="TextBlock">
                <Setter Property="Visibility" Value="Visible"/>
                <Style.Triggers>
                  <DataTrigger Binding="{Binding SourcePath}" Value="">
                    <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                  <DataTrigger Binding="{Binding SourcePath}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Collapsed"/>
                  </DataTrigger>
                </Style.Triggers>
              </Style>
            </TextBlock.Style>
          </TextBlock>
          <TextBlock Text="Source not set" Foreground="LightGray" FontStyle="Italic">
            <TextBlock.Style>
              <Style TargetType="TextBlock">
                <Setter Property="Visibility" Value="Collapsed"/>
                <Style.Triggers>
                  <DataTrigger Binding="{Binding SourcePath}" Value="">
                    <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                  <DataTrigger Binding="{Binding SourcePath}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Visible"/>
                  </DataTrigger>
                </Style.Triggers>
              </Style>
            </TextBlock.Style>
          </TextBlock>
        </Grid>
      </Border>
    </DockPanel>
    <ListView ItemsSource="{Binding Images}" 
              ScrollViewer.VerticalScrollBarVisibility="Visible" />
  </DockPanel>
</DataTemplate>

I hope that the examples here

  • give some insight on how you too can unit test your code,
  • show why a model is a better option for your WPF applications as you can test them
  • give you the courage to write multi threaded code in your WPF models in the knowledge that you can test it

Next we discover that sometimes controls can cause an unresponsive UI in Part 6.

Previous - Responsive WPF User Interfaces Part 4 - Multi-threaded Programming in WPF.

Back to series Table Of Contents

Working version of the code can be found here

Monday, February 2, 2009

Responsive WPF User Interfaces Part 4

Multi-threaded Programming in WPF

In the last post (Part 3) of the series we found a way to get our UI responsive and provide some visual clue to the user that work was being performed. However we found that there were limitations to the model proposed as only small/fast pieces of work could be performed. Once a task started taking a longer time to process, the User Interface (UI) became unresponsive again. This was because the task were still being performed on the UI thread via the dispatcher. This post looks at how we can leverage the multi-threaded functionality in the .NET framework to perform tasks that have a long or unknown duration while keeping the UI responsive.
The .NET framework offers several ways to invoke work on a separate thread including:
  • The Start method on a System.Threading.Thread object
  • BeginInvoke Method on a delegate
  • QueueUserWork method on the System.Threading.ThreadPool type
  • The RunWorkerAsync method on a System.ComponentModel.BackgroundWorker
The merits of each of these is beyond the scope of this post. I am going to pick one and run with it. Ideally I will discuss the merits in another post.

A quick code refactor*

I think it is fair to point out at the moment the adding concurrency handling to our code behind on the Window is going to balloon out the code. This concerns me as I think we already have a problem with single responsibility. The window code is clearly at the presentation layer. It has some properties relevant to the Presentation layer and then has some very specific code regarding the File System. I would like to move that code out in to a model. Doing so will realise several benefits:
  • The code will have a single responsibility (to serve as a presentation model)
  • I should be come testable
  • Ideally the implementation details could become pluggable (swap file system for SkyDrive, flickr, Database etc for example). However this is a benefit, not a goal of the changes we are about to make.
So in this next piece of code I will take the key parts that I want to keep in the model
  • the Images property (ObservableCollection<String>)
  • the IsLoading property (bool)
  • the SourcePath property (string)
  • an implementation of INotifyPropertyChanged interface
  • a new SetSourcePathCommand property. I prefer Commands to event handlers. Using a command will allow us to stop the user changing the Source while we are processing.
At a glance the new code will look like this:
public class PhotoAlbumModel : INotifyPropertyChanged
{
  #region Fields ...

  public ObservableCollection<string> Images {...}
  public bool IsLoading {...}
  public string SourcePath {...}

  public ICommand SetSourcePathCommand { get { return setSourcePathCommand; } }

  #region INotifyPropertyChanged Members...
}

For this example I will use a custom implementation of ICommand that allows me to specify the delegates for the CanExecute and Execute methods. This implementation is called a DelegateCommand. There is an implementation of this in the Composite Application Guidance that I would recommend you use in your code. So I update my Model to use a delegate command and add two methods to handle the CanExecute and Execute behaviour.
private readonly DelegateCommand setSourcePathCommand;
public PhotoAlbumModel()
{
  setSourcePathCommand = new DelegateCommand(ExecuteSetSourcePath, CanSetSourcePath);
  this.PropertyChanged += (sender, e) => { if (e.PropertyName == "IsLoading") setSourcePathCommand.OnCanExecuteChanged(); };
}
#region Command handlers
void CanSetSourcePath(object sender, CanExecuteEventArgs e)
{
  e.CanExecute = !IsLoading;
}

void ExecuteSetSourcePath(object sender, ExecutedEventArgs e)
{
  System.Windows.Forms.FolderBrowserDialog openFolderDlg = new System.Windows.Forms.FolderBrowserDialog();
  openFolderDlg.RootFolder = Environment.SpecialFolder.Desktop;
  if (openFolderDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  {
    if (!string.IsNullOrEmpty(openFolderDlg.SelectedPath))
    {
      SourcePath = openFolderDlg.SelectedPath;
      StartLoadingFiles(SourcePath);
    }
  }
}
#endregion

Bear with me during this reshaping of our code. I will get back to Responsive UIs, but I believe this is important. In our implementation of the Execute handler we call a method we have yet to create StartLoadingFiles. This is where our threading code will be isolated to. I think this makes our code much easier to understand, as so far the class is comprised of simple concepts like Properties, Notification Events & Commands. In our new StartLoadingFiles method we introduce our first Multi-threaded code by implementing a BackgroundWorker.
#region Private method
void StartLoadingFiles(string path)
{
  IsLoading = true;
  BackgroundWorker fileWorker = new BackgroundWorker();
  fileWorker.DoWork += (sender, e) =>
  {
    LoadPath(path);
  };
  fileWorker.RunWorkerCompleted += (sender, e) =>
  {
    IsLoading = false;
  };
  fileWorker.RunWorkerAsync();
}

void LoadPath(string path)
{
  IEnumerable<string> files = fileService.ListFiles(path, IsImage);
  foreach (string item in files)
  {
    dispatcher.Invoke(new Action(() => { Images.Add(item); }));
  }

  IEnumerable<string> directories = fileService.ListDirectories(path);
  foreach (string item in directories)
  {
    LoadPath(item);
  }
}

bool IsImage(string file){...} //Same implementation as before.
#endregion

I hope you agree that so far the code is fairly simple to read and understand Ed-yes it still could be better, no it was not driven out by tests. We have introduced 3 methods here. One is our old friend IsImage that we stole from previous code. StartLoadingFiles method is our entry point to running the Multi-threaded code, and the LoadPath method is the code that will be executed from the BackgroundThread. Four things I would like to point out at this point:
  1. The implementation of the BackgroundWorker is just one example of how you could kick off parallel execution. Don't take this method as best practice.
  2. Note the use of dispatcher.Invoke in the for loop of the LoadPath method. We cant use BeginInvoke in a loop as the variable [item] is passed by ref. If we did, we would be setting the value of a reference type, passing the reference type to a the non-blocking call and then changing the value of the reference type by moving to the next value in the enumeration. This would result in most of the values queued on the dispatcher to just the value of the last item in the enumeration.
  3. We have access to a private variable called dispatcher. Where did that come from? Will cover that in a moment.
  4. We are calling ListFiles and ListDirectories methods on a variable called fileService. Operations on the file system have been delegated to another class now.

To actually make the new code work we have to introduce some new fields and create a constructor that enables these fields to be set.

#region Fields
private readonly Dispatcher dispatcher;
private readonly IFileService fileService;
private readonly DelegateCommand setSourcePathCommand;
private readonly ObservableCollection<string> images = new ObservableCollection<string>();
private string sourcePath;
private bool isLoading = false;
#endregion

public PhotoAlbumModel(Dispatcher dispatcher, IFileService fileService)
{
  this.dispatcher = dispatcher;
  this.fileService = fileService;
  setSourcePathCommand = new DelegateCommand(ExecuteSetSourcePath, CanSetSourcePath);
  this.PropertyChanged += (sender, e) => { if (e.PropertyName == "IsLoading") setSourcePathCommand.OnCanExecuteChanged(); };
}


This also indicates that we have a new Interface of IFileService and lets give you an implementation of that interface FileSystemService.

public interface IFileService
{
  IEnumerable<string> ListFiles(string path, Predicate<String> filter);
  IEnumerable<string> ListDirectories(string path);
}
public class FileSystemService : IFileService
{
  public IEnumerable<string> ListFiles(string path, Predicate<string> filter)
  {
    Collection<string> images = new Collection<string>();
    try
    {
      string[] files = System.IO.Directory.GetFiles(path);
      foreach (string file in files)
      {
        if (filter(file))
          images.Add(file);
      }
    }
    catch (UnauthorizedAccessException) { } //Swallow
    return images;
  }

  public IEnumerable<string> ListDirectories(string path)
  {
    try
    {
      return System.IO.Directory.GetDirectories(path);
    }
    catch (UnauthorizedAccessException)
    {
      return new List<string>().ToArray();
    }
  }
}

I think that is a nice piece of code now Ed-still could be better. Classes are fairly cohesive and their intent I think is fairly.

Presentation

Now how do we display this "Model"? Assuming the reader is an Intermediate WPF developer, they will know what a DataTemplate is. For the example I will create a window and add a DataTemplate that looks very similar to the XAML from previous versions of the code.

<Window x:Class="ArtemisWest.Demo.ResponsiveUI._4_MultiThreaded.WindowHost"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ArtemisWest.Demo.ResponsiveUI._4_MultiThreaded"
        Title="WindowHost" Height="300" Width="300">
  <Window.Resources>
    <DataTemplate DataType="{x:Type local:PhotoAlbumModel}">
      <DataTemplate.Resources>
        <BooleanToVisibilityConverter x:Key="boolToVisConverter"/>
      </DataTemplate.Resources>
      <DockPanel>
        <DockPanel DockPanel.Dock="Top">
          <ProgressBar IsIndeterminate="True" Height="18" DockPanel.Dock="Top" Visibility="{Binding Path=IsLoading, Converter={StaticResource boolToVisConverter}}" />
          <Button x:Name="SetSourcePath" Command="{Binding Path=SetSourcePathCommand}" DockPanel.Dock="Right">Set Source</Button>
          <Border BorderThickness="1" BorderBrush="LightBlue" Margin="3">
            <Grid>
              <TextBlock Text="{Binding SourcePath}">
        <TextBlock.Style>
          <Style TargetType="TextBlock">
            <Setter Property="Visibility" Value="Visible"/>
            <Style.Triggers>
              <DataTrigger Binding="{Binding SourcePath}" Value="">
                <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
              <DataTrigger Binding="{Binding SourcePath}" Value="{x:Null}">
                <Setter Property="Visibility" Value="Collapsed"/>
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </TextBlock.Style>
              </TextBlock>
              <TextBlock Text="Source not set" Foreground="LightGray" FontStyle="Italic">
        <TextBlock.Style>
          <Style TargetType="TextBlock">
            <Setter Property="Visibility" Value="Collapsed"/>
            <Style.Triggers>
              <DataTrigger Binding="{Binding SourcePath}" Value="">
                <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
              <DataTrigger Binding="{Binding SourcePath}" Value="{x:Null}">
                <Setter Property="Visibility" Value="Visible"/>
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </TextBlock.Style>
              </TextBlock>
            </Grid>
          </Border>
        </DockPanel>
        <ListView ItemsSource="{Binding Images}" ScrollViewer.VerticalScrollBarVisibility="Visible" />
      </DockPanel>
    </DataTemplate>
  </Window.Resources>
</Window>

The only real change we have done here is the introduction of a DataTemplate and the use of a Command instead of the Click event handler.

Testing responsiveness

If we run this code we again get a similar feel to the responsive UI we produced in the last post. We were able to make the code from the previous post less responsive by throwing in some Thread.Sleep method calls. The idea here was to replicate some sort of IO delay (disk, network, database etc). To even the playing field lets do the same to our new FileSystemService class.

public IEnumerable<string> ListFiles(string path, Predicate<string> filter)
{
  System.Threading.Thread.Sleep(1000);
 
public IEnumerable<string> ListDirectories(string path)
{
  System.Threading.Thread.Sleep(1000);

Running this code now shows a marked improvement on our previous example. While adding the sleep method makes the code painfully slow to run, the UI is unaffected and happily will respond to interaction without becoming jittery or showing other signs of UI lag.
I do apologise for the rather wide berth we took on this post. This is showcases when one strategy will take you so far but a step back is needed to allow for progress with a better strategy.

*It is not really refactoring but as my friend Grae calls it refuctoring (ie refactoring with out tests as our safety net. We cross our fingers and hope we didn't break anything).

Considering that we have made some changes that are intended to allow for testable code, and that by our own admission we were refuctoring not refactoring I think the next obvious path to take is to see if we can drive out a similar model to what we have through tests. This will also force us to tackle some unknowns about unit testing when the dispatcher is involved.

Next - Creating tests for our multi-threaded WPF code in Part 5

Previous - "User feedback and the limitations of the Dispatcher" in Part 3

Back to series Table Of Contents

Working version of the code can be found here

Sunday, February 1, 2009

Responsive WPF User Interfaces Part 3

User feedback and the limitations of the Dispatcher

In the previous post in this series we found out how fairly innocent code can cause mayhem on our User Interface (UI), locking it up and leaving the user wondering what is going on. We discovered how the Dispatcher can help us out here by queuing small requests for it to do when it can. However, I claimed at the end of the session that the model wont scale well. Well here I will try to actually prove my claim and guide you through some fairly standard functionality that you may have to build in WPF.
Consider a simple Photo viewing application. The application is to load all images in a directory and it's subdirectories. The images are then to be displayed in some fashion. Pretty simple? Here is my first attempt at the presentation in XAML:

<Window x:Class="ArtemisWest.Demo.ResponsiveUI._3_SingleThreaded.SingleThreaded"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Single Threaded">
  <DockPanel>
    <DockPanel DockPanel.Dock="Top">
      <Button x:Name="SetSourcePath" Click="SetSourcePath_Click" DockPanel.Dock="Right">Set Source</Button>
      <Border BorderThickness="1" BorderBrush="LightBlue" Margin="3">
        <Grid>
          <TextBlock Text="{Binding SourcePath}">
            <TextBlock.Style>
              <Style TargetType="TextBlock">
                <Setter Property="Visibility" Value="Visible"/>
                <Style.Triggers>
                  <DataTrigger Binding="{Binding SourcePath}" Value="">
                    <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                  <DataTrigger Binding="{Binding SourcePath}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Collapsed"/>
                  </DataTrigger>
                </Style.Triggers>
              </Style>
            </TextBlock.Style>
          </TextBlock>
          <TextBlock Text="Source not set" Foreground="LightGray" FontStyle="Italic">
            <TextBlock.Style>
              <Style TargetType="TextBlock">
                <Setter Property="Visibility" Value="Collapsed"/>
                <Style.Triggers>
                  <DataTrigger Binding="{Binding SourcePath}" Value="">
                    <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                  <DataTrigger Binding="{Binding SourcePath}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Visible"/>
                  </DataTrigger>
                </Style.Triggers>
              </Style>
            </TextBlock.Style>
          </TextBlock>
        </Grid>
      </Border>
    </DockPanel>
    <ListView ItemsSource="{Binding Images}" ScrollViewer.VerticalScrollBarVisibility="Visible" />
  </DockPanel>
</Window>

Fairly simple. I have

  • TextBlock that shows the Root path of where our images are being fetched from
  • Button to open the folder browser which sets the TextBlock value
  • ItemsControl to show the images that have been fetched (well the filenames of them at the moment)
  • Most of the code here is actually an attempt to display the text "Source not set" when the SourcePath is null or an empty string.

And here is the C# code to do the work:

public partial class SingleThreaded : Window, INotifyPropertyChanged
{
private string sourcePath;
private readonly ObservableCollection<string> images = new ObservableCollection<string>();

public SingleThreaded()
{
  InitializeComponent();
  this.PropertyChanged += SingleThreaded_PropertyChanged;
  this.DataContext = this;
}

public string SourcePath
{
  get { return sourcePath; }
  set
  {
    sourcePath = value;
    OnPropertyChanged("SourcePath");
  }
}

public ObservableCollection<string> Images
{
  get { return images; }
}

void SetSourcePath_Click(object sender, RoutedEventArgs e)
{
  System.Windows.Forms.FolderBrowserDialog openFolderDlg = new System.Windows.Forms.FolderBrowserDialog();
  openFolderDlg.RootFolder = Environment.SpecialFolder.Desktop;
  if (openFolderDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  {
    if (!string.IsNullOrEmpty(openFolderDlg.SelectedPath))
    {
      SourcePath = openFolderDlg.SelectedPath;
    }
  }
}

void SingleThreaded_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if (e.PropertyName == "SourcePath")
  {
    //LoadImages(SourcePath); //Learning from previous experiance we use BeginInvoke instead.
    Dispatcher.BeginInvoke(new LoadImagesDelegate(LoadImages), DispatcherPriority.Background, SourcePath);
  }
}

delegate void LoadImagesDelegate(string folderLocation);
void LoadImages(string folderLocation)
{
  try
  {
    string[] files = System.IO.Directory.GetFiles(folderLocation);
    foreach (string file in files)
    {
      if (IsImage(file))
        Images.Add(file);
    }
  }
  catch (UnauthorizedAccessException) { } //Swallow

  try
  {
    string[] folders = System.IO.Directory.GetDirectories(folderLocation);
    foreach (string folder in folders)
    {
      //LoadImages(folder);//Learning from previous experiance we use BeginInvoke instead.
      Dispatcher.BeginInvoke(new LoadImagesDelegate(LoadImages), DispatcherPriority.Background, folder);
    }
  }
  catch (UnauthorizedAccessException) { } //Swallow
}

bool IsImage(string file)
{
  string extension = file.ToLower().Substring(file.Length - 4);
  switch (extension)
  {
    case ".bmp":
    case ".gif":
    case ".jpg":
    case ".png":
      return true;
    default:
      return false;
  }
}

#region INotifyPropertyChanged Members
/// <summary>
/// Implicit implementation of the INotifyPropertyChanged.PropertyChanged event.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Throws the <c>PropertyChanged</c> event.
/// </summary>
/// <param name="propertyName">The name of the property that was modified.</param>
protected void OnPropertyChanged(string propertyName)
{
  PropertyChangedEventHandler handler = PropertyChanged;
  if (handler != null)
    handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}

This is a little bit more complicated than previous examples. Let's start with the properties:

  • SourcePath is a string property with Notification enabled. This is what the TextBlock is bound to
  • Images is an ObservableCollection of strings. As we modify this collection the UI should update automatically.
Next, the event handlers:
  • SetSourcePath_Click handles the button click and shows an Open Folder dialog box. This allows the user to pick a folder to start looking for images in.
  • SingleThreaded_PropertyChanged handles all PropertyChanged events on itself. A bit silly but it will do for kicking off the loading of images when the SourcePath property changes
Finally the helper methods
  • LoadImages (with it's LoadImagesDelegate) does the grunt work for the class. It looks in a given directory for Images, adds them to the Images collection and then recursively calls itself with the Dispatcher.BeginInvoke
  • IsImage method is a simple method for identifying if a file is an image.

If you run this example the result is actually quite neat. Files start appearing as they are found and the UI feels responsive.

However, if I pick a folder with lots of stuff in it eg: the root of C:\, the process can take quite a while. What would be nice is if I could have some kind of visual clue that tells me when it is complete, or how much is left to go, or anything! Well lets look at our code. I think the simple option would be to have a boolean property call IsLoading that is simply set to true when we are loading images. When we are done set it back to false. So we drop this new piece of code into our C# which gives us the property we are looking for.

private bool isLoading = false;
public bool IsLoading
{
  get { return isLoading; }
  set
  {
    isLoading = value;
    OnPropertyChanged("IsLoading");
  }
}

I now want to modify my UI to show that something is loading. The easiest thing for me to do is throw a ProgressBar control in. So I put this code in just above my button at the top of the second DockPanel:

<Window ...>
  <Window.Resources>
    <BooleanToVisibilityConverter x:Key="boolToVisConverter"/>
  </Window.Resources>
  <DockPanel>
    <DockPanel DockPanel.Dock="Top">
      <ProgressBar IsIndeterminate="True" Height="18" DockPanel.Dock="Top" Visibility="{Binding Path=IsLoading, Converter={StaticResource boolToVisConverter}}" />
      <Button x:Name="SetSourcePath" ...../>

Ok. Now back to our C# code. When the SourcePath changes I want to load the images. This seems like a good place to set our IsLoading property.

void Stackoverflow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if (e.PropertyName == "SourcePath")
  {
    IsLoading = true;
    Dispatcher.BeginInvoke(new LoadImagesDelegate(LoadImages), DispatcherPriority.Background, SourcePath);
    IsLoading = false;
  }
}

Hang on...when we call LoadImages we do it with a BeginInvoke method which is non-blocking. This effectively sets IsLoading to true, then back to false almost immediately. That's not right. Well lets change the BeginInvoke to Invoke. Nope that is not quite right either. Changing the initial BeginInvoke to a Invoke method call will just set the IsLoading flag for the duration where we processed the root of the source path but not for any of its sub-directories. Ok next idea.... Let's change the BeginInvoke inside the LoadImages method to be an Invoke call as well. Right that will work. We run that and it actually works quite well. I get feedback that the system is processing data via the ProgressBar and when the processing is complete, the ProgressBar disappers.

void Stackoverflow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if (e.PropertyName == "SourcePath")
  {
    IsLoading = true;
    Dispatcher.Invoke(new LoadImagesDelegate(LoadImages), DispatcherPriority.Background, SourcePath);
    IsLoading = false;
  }
}

delegate void LoadImagesDelegate(string folderLocation);
void LoadImages(string folderLocation)
{
  try
  {
    string[] files = System.IO.Directory.GetFiles(folderLocation);
    foreach (string file in files)
    {
      if (IsImage(file))
        Images.Add(file);
    }
  }
  catch (UnauthorizedAccessException) { } //Swallow

  try
  {
    string[] folders = System.IO.Directory.GetDirectories(folderLocation);
    foreach (string folder in folders)
    {
      Dispatcher.Invoke(new LoadImagesDelegate(LoadImages), DispatcherPriority.Background, folder);
    }
  }
  catch (UnauthorizedAccessException) { } //Swallow
}

But wait, this exactly follows the StackOverflow model we had in Part 2 of the series. On my system I don't actually have a deep enough directory structure that would cause an overflow (I don't think vista actually supports structures that deep). This concerns me that I am using a system that "could" just cause a StackOverflowException on me. Is my concern well founded? Maybe it is, maybe not. Conceding that a minor concern is not usually good grounds for declaring a proven practice let's consider the performance of our LoadImages method. What would happen if

  • the disk was under load?
  • the call was to a database or remote service (WCF, Web Service, etc...)
  • any other scenario where a method may take more than say 250ms

Well we can emulate the above scenarios by throwing in a Thread.Sleep in to our code. In my code I place a Thread.Sleep(300) into my LoadImage method at the start of the method. This represents latency of the disk/database/network. I think you may agree that 300ms is plausible value for latency on a slow network.

delegate void LoadImagesDelegate(string folderLocation);
void LoadImages(string folderLocation)
{
  Thread.Sleep(300);
  try
  {

If I run my updated code now the result is not good. The progress bar is jumpy, hovering over the button gives a delayed MouseOver effect and clicking on the items in the list is unresponsive.
What have we proved here?

  • The dispatcher can be used for very fast methods to interact with UI elements
  • Using Dispatcher.Invoke method is a option when we actually want the properties of a blocking call
  • The responsiveness of our application can be effected by external interfaces when we call them via the Dispatcher.

So where are we now? Hopefully you have a better understanding of the dispatcher. We like the dispatcher but we are aware of its features and its limitations. The following should be well understood now:

  1. We can achieve simple rendering effects with StoryBoards. These can be asynchronous and WPF does all the hard work to ensure a responsive UI.
  2. The UI is single threaded and performing work on the UI thread can kill UI responsiveness.
  3. The UI thread will have a Dispatcher to enable queuing of work for it to do.
  4. A Dispatcher can only belong to one thread.
  5. UI Controls are DispatcherObjects and can only be updated on the thread they were created on. Conveniently they also expose a property to the Dispatcher that owns them.
  6. You can queue tasks for the UI thread by using the Invoke and BeginInvoke methods on the thread's Dispatcher.
  7. When queuing tasks for the dispatcher you can set a priority for the task.
  8. Dispatcher.Invoke method queues a task and waits for its completion. Dispatcher.BeginInvoke queues a task and does not wait for its completion.
  9. Any task queued for the UI thread will be completed in its entirety before performing any other task. Therefore all tasks performed by the Dispatcher will cause some loss in responsiveness on the UI.

So basically we have arrived at a point where understand the WPF threading model enough to recognise that tasks with long or unknown delays cannot be perform on the UI thread, either via Control Constructors, event handlers, Dispatcher.Invoke or Dispatcher.BeginInvoke. This leads us to our next part in the series Multi-Threaded programming in WPF

Next we discover how to perform true multi-threaded programming in WPF to really get the UI responsive in Part 4.

Previous - The WPF Threading model and the dispatcher in Responsive WPF User Interfaces in Part 2.

Back to series Table Of Contents

Working version of the code can be found here

Tuesday, January 20, 2009

Responsive WPF User Interfaces Part 2

WPF threading model

While WPF is referred to as a Single Threaded model, WPF actually implements a model where by default two threads are used. One thread is used for rendering the display (Render thread), the other thread is used to manage the User Interface (UI Thread). The Render thread effectively runs in the background and you don't have to worry about it, while the UI thread receives input, handles events, paints the screen, and runs application code.

The Dispatcher

The UI thread in WPF introduces a concept called a dispatcher. For a while it eluded me exactly what the dispatcher was and I had elevated it to the realm of magic. However I now like to think of it as a system made up of

  • a worker,
  • a set of prioritised queues
  • and tasks that are allocated to the queues

The best example I can give of the dispatcher is my Cinema example as follows. When I go to my local cinema, they have 3 queues:

  1. "Normal"
  2. "Pre-booked Internet sales"
  3. and "Gold Class"

Obviously you can only queue up in the pre-booked queue if you have a receipt from the web site, and queuing in the "Gold Class" line means I will pay quite a lot more than the standard punters. This makes it easy for the staff to allocate their priorities to the queues. "Gold class" spend the most so get the first priority. "Internet sales" are the next priority as they have already paid. The normal queue is last priority. Now for a moment imagine if there was only one staff member manning the counter at the cinema. The staff member would deal with all of the Gold Class customers, and then all the Internet sales, and then the normal sales. If at any time more customers joined the Gold Class queue, the staff member would complete the current sale and then tend to the higher priority customer. This is in essence what the dispatcher does. The dispatcher can perform one task at a time and decides on which task to complete next based on the priority of the task.
UI threads must have a dispatcher and a dispatcher can belong to only one thread. All controls in WPF are eventually a sub-class of DispatcherObject.
Simplified view of the object hierarchy of a control:

  • Object
    • DispatcherObject
      • DependencyObject
        • Visual
          • UIElement
            • FrameworkElement
              • Control
This is useful to know as a DispatcherObject (and therefore any Control) can only be accessed on the thread it was created on. This prevents us from making calls to a background thread and then updating the UI from that background thread. This is because it would cause concurrency and contention issues. It would also undermine the Dispatcher and it prioritisation system. We can work however, with the dispatcher and its queuing mechanisms to achieve our goal.

Example - Asynchronous Single-threaded Programming

Now that we are familiar with the WPF threading model and Dispatchers we will start to look at some code and hypothesise as to the result of various coding styles. Let us start with a very simple example: a "prime number finder".
This is the basic XAML code that we will use.

<Window x:Class="ArtemisWest.Demo.ResponsiveUI._2_Dispatcher.Unresponsive"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="UnResponsive" Height="300" Width="300">
  <Grid>
    <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
      <Label Margin="6" Padding="0" VerticalAlignment="Bottom" >Next prime number:</Label>
      <TextBlock Margin="6" Text="{Binding Path=PrimeNumber}" Width="100" VerticalAlignment="Bottom" />
      <Button x:Name="StartStop" Click="StartStop_Click" Margin="6" VerticalAlignment="Bottom">
        <Button.Style>
          <Style TargetType="Button" >
            <Setter Property="Content" Value="Start"/>
            <Style.Triggers>
              <DataTrigger Binding="{Binding Path=IsProcessing}" Value="True">
                <Setter Property="Content" Value="Stop"/>
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </Button.Style>
      </Button>
    </StackPanel>
  </Grid>
</Window>

We define a Label, Textbox bound to our current PrimeNumber and a Button to start and stop processing.
The code-behind looks like this:

using System.ComponentModel;
using System.Windows;

namespace ArtemisWest.Demo.ResponsiveUI._2_Dispatcher
{
  public partial class Unresponsive : Window, INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    private int primeNumber = 1;
    private bool isProcessing;

    public Unresponsive()
    {
      InitializeComponent();
      this.DataContext = this;
    }

    public int PrimeNumber
    {
      get { return primeNumber; }
      set
      {
        primeNumber = value;
        if (PropertyChanged != null)
          PropertyChanged(this, new PropertyChangedEventArgs("PrimeNumber"));
      }
    }

    public bool IsProcessing
    {
      get { return isProcessing; }
      set
      {
        isProcessing = value;
        if (PropertyChanged != null)
          PropertyChanged(this, new PropertyChangedEventArgs("IsProcessing"));
      }
    }

    private void StartStop_Click(object sender, RoutedEventArgs e)
    {
      IsProcessing = !IsProcessing;
      int i = PrimeNumber;
      while (IsProcessing && i < 30000)
      {
        i++;
        if (IsPrime(i))
        {
          PrimeNumber = i;
        }
      }
    }

    private bool IsPrime(int number)
    {
      bool result = true;
      for (int i = 2; i < number; i++)
      {
        if (number % i == 0)
        {
          result = false;
          break;
        }
      }
      return result;
    }
  }
}

The important parts of the code here are the two methods near the end of the class; StartStop_Click and IsPrime. StartStop_Click is the event handler for the Button. It basically iterates from the value of the current prime number, up in increments of 1 looking for a new prime number. The IsPrime method does the work to figure out if the value is in fact a prime.
The while loop in the event handler will run until the IsProcessing is set to false or till it hits the upper limit I have set.
Can you spot the problem with this code? 
Basically this code is all performed on the UI thread. Remember back to our Cinema Queue example where the sole staff member would deal with clients in a prioritised way. Also remember that the staff member always finished with the customer before re-evaluating whether there was a higher priority customer. Now consider the code we have here. When the user clicks the button, WPF will queue the event handler to be processed (probably after the render effects of a pressed button). Then the event handler leaps into the While loop updating the PrimeNumber property as it finds new values. Each of these updates to the PrimeNumber property fires a PropertyChanged event which in turns queues tasks for the UI to render the changes to the binding. But wait! While rendering should take a higher priority than the work we are doing, we have not signaled to the Dispatcher that we are done. So the Dispatcher is stuck dealing with our While Loop. Now what happens is we sit there processing prime numbers until we hit our upper limit. We don't even give the Dispatcher a chance to evaluate if we have clicked the button again to stop the process.
So how do we get around this? Well why don't we play nice and instead of being the customer that goes to the "Normal queue" and orders tickets, popcorn and soda for all of their friends and family, we could just order what we need, then rejoin the queue if we want to order some more tickets. Ed-Not such a good analogy. Well we know that we can queue work with the dispatcher and that would let it prioritise our work.

Let's consider the following changes to our C# code:

private readonly DispatcherPriority priority = DispatcherPriority.Normal;

//only required if you are using the old delegate method. If using Action<T> or lambdas this can be removed delegate void PrimeProcessDelegate(int number); private void StartStop_Click(object sender, RoutedEventArgs e) { IsProcessing = !IsProcessing; //Old way of invoking with a delegate PrimeProcessDelegate action = ProcessPrimesFrom; Dispatcher.BeginInvoke(action, priority, PrimeNumber); } private void ProcessPrimesFrom(int number) { if (IsPrime(number)) { PrimeNumber = number; } if (IsProcessing && number < 30000) { //Fancy new syntax for invoking a delegate Action<int> action = new Action<int>(ProcessPrimesFrom); Dispatcher.BeginInvoke(action, priority, number + 1); } } private bool IsPrime(int number) { bool result = true; for (int i = 2; i < number; i++) { if (number % i == 0) { result = false; break; } } return result; }

Here I still have the IsPrime method and the StartStop_Click event handler, but I have changed the code in the event handler and added a new method. Instead of using a While loop, I now ask the Dispatcher to queue a delegate for me. This delegate does my work and then recursively asks the Dispatcher to queue further calls to it. I have also explicitly specified the priority of the task. In our cinema example we had 3 queues, the Dispatcher has 12! The dispatcher has 10 levels of priority (12 if you include Invalid and Inactive) that can conceptually be thought of as priority queues. These priorities are defined by the enumeration DispatcherPriority.

Running the new version of the code you will see what I was hoping to produce in the first example. The start button kicks off the process, prime numbers are discovered, and the UI is refreshed automatically. I can also click the button to toggle processing. Yay!

SIDEBAR: Note that we have used the Dispatcher.BeginInvoke method for queuing our tasks. The Dispatcher also has a method called Invoke that takes the same argument parameters. What is the difference and can we use either? Well BeginInvoke is a non-blocking call and Invoke is a blocking call. This means that calling BeginInvoke essentially throws the task on the Dispatcher's queue and moves on. The Invoke method however places the task on the queue and then patiently waits until the task has been processed before moving on. If we were to substitute the BeginInvoke method with the Invoke method we would most probably get a StackOverflowException. So you can't just use either method you should use the right tool for the job. Later in the series we will use the Invoke method.

So that's it? That is all there is to Responsive UI's in WPF? No. We are about half way. :-(
The example we just worked through works well but will not scale. Remember that the dispatcher is bound to one thread. So all of our work is being done on one thread. Also remember that if any of the work took a while (like if IsPrime was called with an argument that was a prime number over 1,000,000) it would lock the UI. Also consider that the next computer you buy will probably have 4+ cores, so why are you only using 1 thread? And lastly consider that as we get better at WPF and more horsepower becomes available (CPUs + GPUs), customers will expect more animation, video, reflections etc... which all must be done on the UI thread. We don't want to bog down our poor old Dispatcher with calculating primes!
But this is good progress. You may find that using the Single Threaded approach we have discovered here, works well for you in some scenarios. These scenarios would be where tiny chunks of work can be done at a time and they are related to the UI/Presentation.

Next we discover how to provide user feedback and the limitations of the Dispatcher in Part 3.

Previous - Responsive WPF User Interfaces Part 1 - Introduction, Declarative Programming and Storyboards.

Back to series Table Of Contents

Working version of the code can be found here