Saturday, June 6, 2009

Responsive WPF User Interfaces Part 7

Creating a responsive Image control

In part 6 we discovered problems even when we follow the guidelines from this series. In this case we find that sometimes it can be the controls themselves that are the cause of the lagging interface. In part 6 it was the Image control that was to blame. I make the assumption that it was trying to perform it's bitmap decoding on my precious dispatcher thread. However not only Image controls can get ugly, Charles Petzold shows some problems you may find when using chart controls (or any Items Control) in his Writing More Efficient Items Controls article.

The first thing I wanted to do was to just sub class Image however, I personally was stumped as to how I would then create my control template for it. Next I actually only wanted to accept URIs as my source so that I can do the decoding from file to an ImageSource myself explicitly and not on the UI thread via a TypeConverter. So I want a sub class of Control with a dependency property of UriSource and then 3 read-only properties ImageSource, IsLoading and HasLoadFailed. UriSource will provide the hook to bind your file name to. ImageSource will then provide the decoded ImageSource to be displayed. IsLoading and HasLoadFailed will be there so that you can update the UI appropriate to the lifecycle of the image.

The Style and control template I came up for this design was this:

<Style TargetType="{x:Type controls:Thumbnail}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type controls:Thumbnail}">
        <Image x:Name="ImageThumbnail"
               Source="{TemplateBinding Image}"
               HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
               VerticalAlignment="{TemplateBinding VerticalAlignment}"
               MaxHeight="{TemplateBinding MaxHeight}"
               MaxWidth="{TemplateBinding MaxWidth}"
               Stretch="Uniform"
               StretchDirection="DownOnly" />
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Where I want to use the Thumbnail control I can have some XAML that hides and shows a loading indicator. This example here just shows the text "Loading Image..." but would probably have a nice animation or an indeterminate progress indicator.

<Border CornerRadius="4" BorderBrush="Silver" BorderThickness="1"
        Width="150"
        Height="150"
        Margin="10">
  <Grid>
    <local:Thumbnail x:Name="Thumbnail"
                     MaxWidth="148"
                     MaxHeight="148"
                     UriSource="{Binding}" 
                     HorizontalAlignment="Center" VerticalAlignment="Center"
                     ToolTip="{Binding}">
      <local:Thumbnail.Style>
        <Style TargetType="{x:Type local:Thumbnail}">
          <Setter Property="Visibility" Value="Visible"/>
          <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsLoading}" Value="True">
              <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </local:Thumbnail.Style>
    </local:Thumbnail>

    <TextBlock Text="Loading image..."
               Foreground="Gray"
               HorizontalAlignment="Center"
               VerticalAlignment="Center">
      <TextBlock.Style>
          <Style TargetType="TextBlock">
              <Setter Property="Visibility" Value="Visible"/>
              <Style.Triggers>
                  <DataTrigger Binding="{Binding ElementName=Thumbnail, Path=IsLoading}" Value="False">
                      <Setter Property="Visibility" Value="Collapsed"/>
                  </DataTrigger>
              </Style.Triggers>    
          </Style>
      </TextBlock.Style>
    </TextBlock>
  </Grid>
</Border>

Well that is the easy bit, the public API. Trust me it gets more interesting.

My gut feeling was basically take the code from reflector and use that to decode Uris to ImageSource objects, but perform the action on a background thread. Two problems here:

  1. Most of the fun code that happens when the URI is being parsed and then decoded, is internal. :-(
  2. You cant pass most sub-classes of ImageSource across threads. If you create them on one thread they cant be accessed on another thread. So this stops us blindly copying code from reflector as all that code only runs on the dispatcher thread so wont face this problem.

We do have something to work with however: WriteableBitmap.

In some vain attempt at brevity (he says in his 7th post in the series!), I will try to skip over all of the brick walls I faced and jump straight to the solution I came up with. This was a great learning experience for me, and in the spirit of a learning experience the code is fairly rough (with TODO comments still intact). I will try to build this control into a stable control but in its current state is very much demo-ware.

First thing I want to achieve was to have the decoding and if possible the reading from disk happen off the UI thread. Lets start with the easy bit; reading the file into memory. I'm going to keep that simple and just go with grabbing the file as a byte array like this

byte[] buffer = File.ReadAllBytes(UriSource);

Next I want to decode the byte array into some form of ImageSource. To do this I have used a combination of the BitmapDecoder and the WriteableBitmap. First I take the byte array and load it into a MemoryStream, and then pass the stream to the BitmapDecoder factory method Create. This will return me an instance of one of its implementations (BitmapDecoder is abstract/MustInherit). From here I make a bold assumption that we only care about the first "Frame" of the image. I believe that most formats don't support multiple frames but formats like GIF do which allows them to have animation features. Anyway, in my demo-ware code I just take the first frame.

using (Stream mem = new MemoryStream(buffer))
{
  BitmapDecoder imgDecoder = null;
  imgDecoder = BitmapDecoder.Create(mem, BitmapCreateOptions.None, BitmapCacheOption.None);

  BitmapFrame frame = imgDecoder.Frames[0];
  double scale = GetTransormScale(maxWidth, maxHeight, frame.PixelWidth, frame.PixelHeight);

  BitmapSource thumbnail = ScaleBitmap(frame, scale);

  // this will disconnect the stream from the image completely ...
  var writable = new WriteableBitmap(thumbnail);
  writable.Freeze();
  return writable;
}

From here I figure out the ratio that I want to scale it to. There doesn't seem much point in returning an 8MB image if we only want to see it as 300x300 does it? Next we request the image as what is almost our final product. We have a helper method scale the frame and return it as a BitmapSource. We can't assign this BitmapSource back to our ImageSource dependency property as they don't play nice over thread boundaries. So, the last thing we need to do is take the BitmapSource we just generated and create a WriteableBitmap from it and then call its Freeze method. This puts the WriteableBitmap in an immutable state which then makes it thread safe. Whew!

Just for reference here is the GetTransformScale method

private static double GetTransormScale(double maxWidth, double maxHeight, double currentWidth, double currentHeight)
{
  double xRatio = maxWidth / currentWidth;
  double yRatio = maxHeight / currentHeight;
  double resizeRatio = (xRatio > yRatio) ? xRatio : yRatio;
  if (resizeRatio > 1)
    resizeRatio = 1;
  return resizeRatio;
}

and the ScaleBitmap method

private static BitmapSource ScaleBitmap(BitmapSource source, double scale)
{
  if (scale > 0.9999 && scale < 1.0001)
  {
    return source;
  }
  var thumbnail = new TransformedBitmap();
  thumbnail.BeginInit();
  thumbnail.Source = source;
  var transformGroup = new TransformGroup();
  transformGroup.Children.Add(new ScaleTransform(scale, scale));
  thumbnail.Transform = transformGroup;
  thumbnail.EndInit();
  return thumbnail;
}

So all things considered, once you get over the decoding stuff and then wrestle with the various subclasses of ImageSource that play their part, its not too bad. But like I said earlier; it gets more interesting. So far we have only looked at how to decode the image, we have yet to consider how to make the call to perform the decoding. My first instinct was to make the call on any change to the UriSource property. However I may not have the MaxHeight and MaxWidth information at that point in time. This caused me much stress over when should I call this decode functionality. If no value is ever going to be set for MaxHeight or MaxWidth then I should just process the image, but if first the UriSource is set then the MaxHeight, then the MaxWidth I would end up creating 3 asynchronous calls to render 3 different sized images. This would be a disaster as it would surely end up with race conditions and most likely the wrong sized image being displayed. The other obvious problem with that is that we would be performing 3 times the work. Hmm.

My solution (and mileage may vary as I am green to concurrent programming models) was to create a stack of render requests for each instance of a Thumbnail. As a property was set then a request would be added to the stack and a request to start processing the stack would occur. Periodically while processing the image I would check to see if the current work was invalidated by any new requests to the stack. If the current request was invalid it would terminate its work. The request to start processing the stack would simply try to pop the last request from the stack and clear out all other requests (effectively ignoring stale requests). On returning from the render request it would loop back and try to pop anything new from the stack. This last part while important is very much implementation details and could vary dramatically from anything you may implement. I have just read over the code myself and I could do with some work. It is amazing what just 3months (and reading Joe Duffy's Concurrent Programming for Windows) does for your appreciation of your code.

As always the code is available for you to have a play. Open it up, pick it to pieces, use what you like. Obviously I take no responsibility for the code if you do choose to use it as this is intended on being a learning exercise. Having said that I do try to produce good code for my demos so if you do see something that is not good enough let me know.

This has been a fun series and I hope you liked it and learnt as much as I did from it.

Back to series Table Of Contents

Working version of the code can be found here

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