Click here to Skip to main content
Click here to Skip to main content

WPF: A Most Useful Threading Component

By , 30 Dec 2009
 

Contents

Introduction

As many of you may know, I write quite a lot about UI work and WPF technology, and I have in the past written articles about Threading, and have also written a great lot about my own WPF MVVM Framework Cinch, which includes a lot of stuff to get you up and running doing WPF the MVVM way.

Well, one thing that has always bugged me while working with WPF is threading and doing something in the background and keeping my UI nice and free to do other stuff. Sure I can spawn a new Thread or use the ThreadPool and even use the funky BackgroundTaskManager class inside of Cinch which does help a lot in terms of fetching data on an internally held BackgroundWorker and is completely unit testable, as discussed in this Cinch article. I just felt that more improvements could be made.

For example, what I would want to be able to do, is have a ViewModel (to allow Binding and testing) that was being used as a DataContext for some View, to be able to accept some parametised work delegate that would be performed on a background thread, and would show some busy animation or status while the threading operation was happening, and would then either show an error status in the View somewhere if the background threading operation failed, or would show me the actual data that the background thread fetched that I requested in the parametised work delegate, if there was no failure while fetching the relevant data.

I should point out that the code contained here does spawn a new BackgroundWorker per threading operation, where each operation is expected to return some single definable bit of data, such as a single List<SomeClass>, or even some other very expensive time consuming data to fetch. My intent was never to have some global threading manager that fetches all and sundry in one hit, it's more micro managing the data.

In laymen's terms, think of it like this (where "I" and "Me" in this italic paragraph means the demo app code provided in this article ):

You want me to show a List<Contact> that could take some time to fetch, fair enough. I'll create a BackgroundWorker to do that and manage that background activity, and let you know how it's going, and when I am done, I'll return you a List<Contact>, or some error message. Oh, and you also want me to show a List<BankAccount> on the same View that will also take some time to fetch. Well now, for that, I will need to return another type of List. In fact, it will be a List<BankAccount> so I am going to be needing another BackgroundWorker to do that, and return you your List<BankAccount>.

I know this could potentially spawn a few threads, but internally, the BackgroundWorker makes use of the ThreadPool, which is ace, so I do not feel it's an issue, as the management of Threads is done for us by the .NET Framework. Obviously, if you have hundreds of lists of data on one View, this article may not be for you at all, and you should stop reading right here. If on the other hand you have a few lists of data or a few expensive bits of data to fetch, this code could well be for you.

Anyway, that is how I see it working, and after messing around for a while, I think I have done just that.

There are a few assumptions that I have made, which are as follows:

  • That people are using WPF, and are pretty competent with it. This is not a beginner article at all.
  • That people are using the MVVM pattern.
  • That people think it's a good idea to have parts of your View show error messages if the data that was supposed to be fetched failed to happen.
  • That people are happy with the idea of an item of data (such as a List of data) or expensive bits of data being fetched by a dedicated BackgroundWorker manager object, which we will get to later.

The Actual Problem

The problem is really like this: from what I have seen, very few people take the time to show the user much feedback at the best of times, let alone when a long running threading operation is happening. In fact, some UIs just boldly do everything on the UI thread, and let the user wait. OK, some people do change the icon to an hour-glass or something, and disable buttons etc., while something is happening.

Wouldn't it be better if we had some component that kept the user in the loop? They start a long running operation either by requesting it or by opening up some View that requires it, and when that happens, the user is constantly shown what is going on as a progress bar is shown while we are doing the work, and if it fails, we show them why, right there in the UI, where it's visible, not in some MessageBox that disappears as soon as they accept "OK", and then try and ring support only to be asked what the MessageBox message said. Oh, I closed that, sorry. And of course, when it all goes to plan, simply hide the progress bar, do not show any failure UI, and just show them what they wanted that has now been fetched.

That is the problem, the way I see it.

The Solution

So what is the solution? Well, the solution is obviously to come up with something that fixes the problem, right?

Well, this article does fix all of the things mentioned above in the problem description. The way it does it, is by using various bits of WPF technology, and threading bits and pieces.

As I previously mentioned, the code supplied in the attached demo app is assuming that you are using the MVVM pattern. I am not saying you could not get it to work without using MVVM; it's just, I think MVVM is fab, and it works, and that is the only way I will be describing in this article's text.

What Does the Solution Look Like

The demo code makes use of a simple idea; we use a WPF UserControl to wrap a bit of content, which would be the UI data that you wish to fetch in a background thread.

So when it runs, it looks something like this:

The attached code makes use of the AdornerLayer to show different Adorners depending on the state of the background threading operation. If the background threading operation is busy, the BusyAdorner is shown. If the background threading operation failed and is not busy, the FailedAdorner is shown. If the background threading operation is not busy and not failed, hide both the BusyAdorner and FailedAdorner, which just leaves the original data shown in the UI, which has now been fetched on a background thread.

You can read more about how the Adorners work in the Adorners section below.

How Does the Solution Work

In this series of sub sections, I will outline how the attached demo code is structured. It should be noted that you will not need to know about a lot of this, you would simply include it in your project and do what is recommended in the How to Go About Using This Idea in Your Own WPF App section. But if you are like me, you will want to know about the details in full before knowing what you need to do to use it, so that is what this section is all about.

Foreword About the Demo App

The demo app attached obviously has to demonstrate the total idea. As such, there is a retarded bit of code that allows the user to pick or choose whether the background threading operation should fail during runtime. The user is able to do this by clicking a button on the demo code's UI.

Now this is obviously only test code, and should never be used in production code; all it does is toggle a "ShouldFail" flag that is checked within the background thread delegate. Which is like this in the demo code:

Func<Dictionary<String, Object>, 
   ThreadableItem<List<StuffData>>> taskFunc = (inputParams) =>
{
    try
    {
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        #region TEST EXAMPLE CODE, YOU SHOULD NOT DO THIS IN PRODUCTION CODE
        if (ShouldFail)
        {
            throw new InvalidOperationException(
                "InvalidOperationException occurred\r\n\r\n" +
                "This Exception has been raised inside " + 
                "the Window1ViewModel delegate " +
                "which is the actual payload for the " + 
                "ThreadableItemViewModel<T> TaskFunc delegate.\r\n\r\n" +
                "Which is obvioulsy not what you would " + 
                "do in a production system.\r\n\r\n" +
                "You would more typically catch your own business Exceptions " +
                "(say from talking to WCF) and then rethrow them. " +
                "This just demomstrated how all this hangs together");
        }
        else
        {
            List<StuffData> data = new List<StuffData>();
            for (int i = 0; i < (Int32)inputParams["loopMax"]; i++)
            {
                data.Add(new StuffData(String.Format("The Text Is {0}",i),i));
                Thread.Sleep(5); //simulate time going by
            }
            //work is done at this point, so return new ThreadableItem with the
            //actual data in it, and no failure message
            return new ThreadableItem<List<StuffData>>(data, String.Empty);
        }
        #endregion
    }
    //something went wrong so return a new ThreadableItem with the failed
    //message in it
    catch(Exception ex)
    {
        return new ThreadableItem<List<StuffData>>(null, ex.Message);
    }
};

It is just there to demonstrate how the code is intended to work. In real production code, you would not do this; you would more likely do something like this:

Func<Dictionary<String, Object>, ThreadableItem<List<Client>>> taskFunc = (inputParams) =>
{
    try
    {
        try
        {
            //Obtain a list of Clients that work in particular BusinessArea 
            Service<IGateway>.Use((client) =>
            {
                RetrieveDataByQueryRequest request = new RetrieveDataByQueryRequest();
                request.Query = new Query().SelectAll(BusinessEntityType.Client)
                    .Where(new Filter("BusinessAreaId", 
                        FieldOpeartor.Equals,(Int32)inputParams["businessArea"]);

                RetrieveDataByQueryResponse response = 
                    (RetrieveDataByQueryResponse)client.Execute(request);

                return new ThreadableItem<List<Client>>(response.Clients, String.Empty);
            });
        }
        //catch WCF FaultExceptions
        catch (FaultException<SerializationException> sex)
        {
            //throw actual Exception which is caught it outer catch
            throw new BusinessException("A serialization issue has occurred");
        }
        //catch WCF FaultExceptions
        catch (FaultException<InvalidArgumentException> iaex)
        {
            //throw actual Exception which is caught it outer catch
            throw new BusinessException("One of the arguments is invalid");
        }

    }
    //something went wrong so return a new ThreadableItem with the failed
    //message in it
    catch(Exception ex)
    {
        return new ThreadableItem<List<Client>>(null, ex.Message);
    }
};

While this may look a bit hairy right now, do not worry, we will be going through that rather nasty looking Func<T,TResult> delegate declaration a bit later. The important thing to note is that the demo code uses some gash test code that you should see as just that demo code that needs replacing with your real code, something like the code chunk seen above.

MVVM

As I stated, the demo code uses the MVVM pattern. You might ask yourself why. The reasons are simple; MVVM allows us to bind directly to our ViewModel, and the ViewModel also serves as a very nice unit testing entry point. As such, there are various ViewModels in the attached demo code, the main ones being:

ViewModelBase: Simple INotifyPropertyChanged base class for all other ViewModels to inherit from.

Here is what this looks like, nothing too fancy:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace ThreadingComponent
{
    /// <summary>
    /// Base class for all ViewModels, simply provides INPC support
    /// </summary>
    public class ViewModelBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notify using pre-made PropertyChangedEventArgs
        /// </summary>
        /// <param name="args"></param>
        protected void NotifyPropertyChanged(PropertyChangedEventArgs args)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, args);
            }
        }

        /// <summary>
        /// Notify using String property name
        /// </summary>
        protected void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

ThreadableItemViewModelBase: This is a base class for ThreadableItemViewModel<T> based ViewModels, and allows the UserControl that handles the AdornerLayer and wraps the data to hook up INotifyPropertyChanged property changed watchers without having to care about the actual generic type of the current background threading operation. This class basically just exposes some common properties that all ThreadableItemViewModel<T> based ViewModels will need to use.

This is what this looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ThreadingComponent
{
    /// <summary>
    /// Provides a base class for <see cref="ThreadableItemViewModelBase">
    /// ThreadableItemViewModel</see> classes. The reason a base class is needed
    /// is that the <see cref="ThreadableHostControl">ThreadableHostControl</see> 
    /// needs to listen to certain INPC value changes to swap the Adorners in and out. 
    /// Now as inheritors of this class use Generics the <see cref="ThreadableHostControl">
    /// ThreadableHostControl</see> needs a common INPC ancestor that it can listen
    /// to INPC property changes from. This base class enables that behaviour.
    /// </summary>
    public class ThreadableItemViewModelBase : ViewModelBase
    {
        #region Data
        private Boolean isBusy = false;
        private Boolean failed = false;
        private String  errorMessage = String.Empty;
        #endregion

        #region Public Properties
        public bool IsBusy
        {
            get { return isBusy; }
            set 
            {
                isBusy = value;
                NotifyPropertyChanged("IsBusy");
            }
        }

        public bool Failed
        {
            get { return failed; }
            set
            {
                failed = value;
                NotifyPropertyChanged("Failed");
            }
        }

        public String ErrorMessage
        {
            get { return errorMessage; }
            set
            {
                errorMessage = value;
                NotifyPropertyChanged("ErrorMessage");
            }
        }
        #endregion
    }
}

ThreadableItemViewModel<T>: This generic ViewModel inherits from ThreadableItemViewModelBase. Its job is to manage the background threading operation. As such, it has various properties that facilitate the management of the running of the background threading activity. The T generic should be the Type that you want for the background threading activity. So for example, if I expected to get a List<Client> back, T would be List<Client>.

You would need to have one of these ThreadableItemViewModel<T> for each background activity you wish to perform. In the demo app, that is only one List of some imaginary Model data called "StuffData", so in my main ViewModel (Window1ViewModel), I have a single instance of ThreadableItemViewModel<List<StuffData>> which is used to manage the retrieval of a List<StuffData>.

Here is what this looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;

namespace ThreadingComponent
{
    /// <summary>
    /// This ViewModel contains all the data required to run a work delegate
    /// (the TaskFunc Func<Dictionary<String,Object>,ThreadableItem<T>>) and also
    /// exposes methods to allow the work delegate to be run on a new thread. The 
    /// Data obtained by running the background thread is exposed on the Data
    /// property, and the BgWorker is also exposed as a property so that it can be
    /// used within Unit tests.
    /// </summary>
    /// <typeparam name="T">The return type of the threading operation</typeparam>
    public class ThreadableItemViewModel<T> : ThreadableItemViewModelBase
    {
        #region Data
        private ThreadableItem<T> data;
        private Func<Dictionary<String,Object>,ThreadableItem<T>> taskFunc;
        private BackgroundTaskManager<ThreadableItem<T>> bgWorker;
        private Dictionary<String, Object> parameters = null;
        #endregion

        #region Private Methods
        /// <summary>
        /// Sets up the actual BackgroundTaskManager<T> component
        /// </summary>
        private void SetupWorker()
        {
            if (taskFunc == null)
                throw new NullReferenceException("TaskFunc can not be null");

            bgWorker = new BackgroundTaskManager<ThreadableItem<T>>(
                 () =>
                 {
                     return taskFunc(Parameters);
                 },
                 (result) =>
                 {
                     Data = result;
                 });

            BgWorker.BackgroundTaskStarted -= BackgroundTaskStarted;
            BgWorker.BackgroundTaskCompleted -= BackgroundTaskCompleted;

            BgWorker.BackgroundTaskStarted += BackgroundTaskStarted;
            BgWorker.BackgroundTaskCompleted += BackgroundTaskCompleted;
        }

        /// <summary>
        /// Event that is raised when the background work is Completed. This event
        /// is raised by the internal BackgroundTaskManager<T> component
        /// </summary>
        private void BackgroundTaskCompleted(object sender, EventArgs args)
        {
            //The order that these properties IS IMPORTANT, as it dictates
            //which Adorner will be shown
            IsBusy = false;
            Failed = !String.IsNullOrEmpty(Data.Error);

        }

        /// <summary>
        /// Event that is raised when the background work is Started. This event
        /// is raised by the internal BackgroundTaskManager<T> component
        /// </summary>
        private void BackgroundTaskStarted(object sender, EventArgs args)
        {
            //The order that these properties IS IMPORTANT, as it dictates
            //which Adorner will be shown
            IsBusy = true;
            Failed = false;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Run the work delegate on a new thread
        /// </summary>
        public void Run()
        {
            SetupWorker();
            bgWorker.RunBackgroundTask();
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// The actual work delegate. This must ALWAYS take a 
        /// Dictionary<String,Object> as the 1st parameter, which is the collection
        /// of parameters that the work delegate may need to do its work. This can obviously
        /// be null. The T is the expected return type for the threading operation
        /// </summary>
        public Func<Dictionary<String,Object>,ThreadableItem<T>> TaskFunc
        {
            set
            {
                taskFunc = value;
            }
        }

        /// <summary>
        /// A Dictionary of key/value pairs, where each pair is a parameter
        /// that the work delegate (TaskFunc) may need to do its work
        /// </summary>
        public Dictionary<String, Object> Parameters
        {
            get { return parameters; }
            set { parameters = value; }
        }

        /// <summary>
        /// The BackgroundTaskManager exposed so you can use it within
        /// Unit tests. See the actual <see cref="BackgroundTaskManager">
        /// BackgroundTaskManager</see> code for how to do that
        /// </summary>
        public BackgroundTaskManager<ThreadableItem<T>> BgWorker
        {
            get { return bgWorker; }
        }

        /// <summary>
        /// The actual Data that is the result of running the 
        /// background threading operation
        /// </summary>
        public ThreadableItem<T> Data
        {
            get { return data; }
            set
            {
                data = value;
                if (data != null)
                    this.ErrorMessage = data.Error;
                NotifyPropertyChanged("Data");
            }
        }
        #endregion
    }
}

The most important things to note in this bad boy are the public properties:

  • TaskFunc: Which is a Func delegate that is the actual background work you want done.
  • Parameters: A Dictionary<String,Object> which is fed into the TaskFunc, Func delegate which is your parameters collection, which may be useful when running the background operation.
  • BgWorker: The actual BackgroundTaskManager<T> which is exposed to allow a Unit Test to maybe set up a AutoResetEvent and only Wait a specific amount of time for the operation to happen, before assuming it failed. The BackgroundTaskManager<T> class is straight out of my Cinch MVVM framework, and is discussed in this Cinch article.
  • Data: Is the actual data which is of Type ThreadableItem<T>, so what does one of those look like then? Well, ThreadableItem<T> is a simple class that represents the result of the background operation, so it has a DataObject and an Error. Only one of which is expected to be an actual value at any one time.

If the operation ran successfully, then the ThreadableItem<T>'s DataObject will be an instance of T that the user asked for, and the ThreadableItem<T>'s Error will be an empty string.

If the operation failed, the ThreadableItem<T>'s DataObject will be null, and the ThreadableItem<T> Error will be an Exception message string.

For clarity, here is what the ThreadableItem<T> class looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ThreadingComponent
{
    /// <summary>
    /// This class represents a threadable item, which can have
    /// both an valid payload dataobject which is specified using
    /// the generic, and also an error string value
    /// </summary>
    /// <typeparam name="T">The type of the return value for the threading
    /// operation that will use a ThreadableItem</typeparam>
    public class ThreadableItem<T> : ViewModelBase
    {
        #region Data
        private T dataObject;
        private String error;
        #endregion

        #region Constructor
        public ThreadableItem(T dataObject, String error)
        {
            this.DataObject = dataObject;
            this.Error = error;
        }
        #endregion

        #region Public Properties
        public T DataObject
        {
            get { return dataObject; }
            set
            {
                dataObject = value;
                NotifyPropertyChanged("DataObject");
            }
        }

        public String Error
        {
            get { return error; }
            set 
            {
                error = value;
                NotifyPropertyChanged("Error");
            }
        }
        #endregion
    }
}

So far, all we have covered in the ViewModels, you will most likely not change, so there is one more to cover which is how to actually use the ones we have seen above. In the demo app, there is a single Window called Window1.xaml, this has a single ViewModel (to allow binding) called Window1ViewModel, which manages all the operations that Window1 wants to do.

Window1ViewModel is an exemple of how to use the rest of the codebase, though as I previously stated, does have test code in it so you can see how all this fits together, and you will need to change it in your production code. Again, I showed you an example of that earlier.

Commanding

Whilst not strictly part of the article's main thrust, I would recommend using some sort of DelegateCommand, or Josh Smith RelayCommand, or Marlon Grech SimpleCommand (which is what I use), all of which allow your UI to bind to a ViewModel exposed ICommand and actually run code in the ViewModel. The reason I would recommend ICommands is that you can disable a button in the ICommand.CanExecute directly in the ViewModel if there is a background threading operation in progress. For an example of this, see Window1ViewModel.

Obviously, this only applies if the threading operation happens as a result of a button click or something the user initiated.

Threading

Since this article is all about a background threading component/idea, you would expect there to be loads to say about Threads. Well, actually a lot of that is abstracted from you.

The steps involved are pretty much like this (you can use the attached demo Window1ViewModel code as a basis for writing your own ViewModel code to use this article's ideas/concepts).

Step 1: Expose property and pick T

Create a ViewModel, and expose a ThreadableItemViewModel<T> as a public property which you can then bind to using a ThreadableHostControl UserControl which is described in the next section, where the generic T must obviously be qualified with the correct type; for example, here is a valid property declaration:

public ThreadableItemViewModel<List<StuffData>> ThreadVM
{
    get { return threadVM; }
}
Step 2: Wire up background work delegate

Now that you have exposed ThreadableItemViewModel<T> as a public property, and picked a return type for it, we can look at how to get the results in a background thread. This is easily achieved; all we have to do is set up the background work delegate, which is done as follows:

Func<Dictionary<String, Object>, ThreadableItem<List<StuffData>>> taskFunc = (inputParams) =>
{
    try
    {
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        // REGION BELOW FOR TESTING ONLY
        #region TEST EXAMPLE CODE, YOU SHOULD NOT DO THIS IN PRODUCTION CODE
        if (ShouldFail)
        {
            throw new InvalidOperationException(
                "InvalidOperationException occurred\r\n\r\n" +
                "This Exception has been raised inside the Window1ViewModel delegate " +
                "which is the actual payload for the " + 
                "ThreadableItemViewModel<T> TaskFunc delegate.\r\n\r\n" +
                "Which is obvioulsy not what you would do in a production system.\r\n\r\n" +
                "You would more typically catch your own business Exceptions " +
                "(say from talking to WCF) and then rethrow them. " +
                "This just demomstrated how all this hangs together");
        }
        else
        {
            List<StuffData> data = new List<StuffData>();
            for (int i = 0; i < (Int32)inputParams["loopMax"]; i++)
            {
                data.Add(new StuffData(String.Format("The Text Is {0}",i),i));
                Thread.Sleep(5); //simulate time going by
            }
            //work is done at this point, so return new ThreadableItem with the
            //actual data in it, and no failure message
            return new ThreadableItem<List<StuffData>>(data, String.Empty);
        }
        #endregion
    }
    //something went wrong so return a new ThreadableItem with the failed
    //message in it
    catch(Exception ex)
    {
        return new ThreadableItem<List<StuffData>>(null, ex.Message);
    }
};

//setup Threading task function
threadVM.TaskFunc = taskFunc;

The important thing to observe here are that we return a ThreadableItem<T> where T is of type List<StuffData>. And remember, a Func<T,TResult> is nothing more than a delegate that looks like this:

public delegate TResult Func<T, TResult>(T arg);

So really, all we are just saying is that we have a delegate that takes Dictionary<String, Object> as an input parameter, and it returns ThreadableItem<List<StuffData>> as a return value. Simple, right?

The Dictionary<String, Object> acts as a collection of parameters that may be needed by the thread work item, so we need to configure it like so:

//setup Threading parameters
Dictionary<String, Object> parameters = new Dictionary<String, Object>();
parameters.Add("loopMax", 30000);
threadVM.Parameters = parameters;

If your work delegate does not require any parameters to do its job, simply set Parameters = null.

Step 3: Run the Background Work Item

The last thing to do is to run the background work item, which is as easy as this:

threadVM.Run();

Which will do the following inside the ThreadableItem<T> code:

/// <summary>
/// Run the work delegate on a new thread
/// </summary>
public void Run()
{
    SetupWorker();
    bgWorker.RunBackgroundTask();
}

/// <summary>
/// Sets up the actual BackgroundTaskManager<T> component
/// </summary>
private void SetupWorker()
{
    if (taskFunc == null)
        throw new NullReferenceException("TaskFunc can not be null");

    bgWorker = new BackgroundTaskManager<ThreadableItem<T>>(
         () =>
         {
             return taskFunc(Parameters);
         },
         (result) =>
         {
             Data = result;
         });

    BgWorker.BackgroundTaskStarted -= BackgroundTaskStarted;
    BgWorker.BackgroundTaskCompleted -= BackgroundTaskCompleted;

    BgWorker.BackgroundTaskStarted += BackgroundTaskStarted;
    BgWorker.BackgroundTaskCompleted += BackgroundTaskCompleted;
}

This code makes use of a funky BackgroundTaskManager class inside of Cinch which does help a lot in terms of fetching data on an internally held BackgroundWorker and is completely unit testable, as discussed in this Cinch article. The important thing to note here is to see how this simply calls the original taskFunc property, which is the one that you just specified using the Func<T,TResult> delegate, remember?

Func<Dictionary<String, Object>, ThreadableItem<List<StuffData>>> taskFunc = (inputParams) =>
{
  ....
  ....
  ....
  ....
};

//setup Threading task function
threadVM.TaskFunc = taskFunc;

It also passes the Func<T,TResult> delegate the parameters Dictionary<String, Object>, which are the parameters that can be used inside the threading delegate work item.

I guess I better prove this all works with a screenshot. Here is a screenshot using the worker item code above:

Word of warning

The code above is just there to demonstrate how the failure code is intended to work. A reader alerted me to the fact that the previous article (yes, this is being posted again) took up 400MB of RAM. Now, the reason that was is my old worker delegate used to create 5,000,000 objects in memory, which as you can imagine was a real issue. Now, we would never actually do that in a production environment. I have modified the code to use a much more modest 30000 objects, which uses like 19MB of RAM. So thanks to that user for spotting that. I am glad it was not something stupid I had done. Phew.

In fact, after I published it again, the same user Insomniac Geek came up with another rather sensible suggestion, which is to use a few items (1000) and use a System.Threading.Thread.Sleep(5) to simulate some work, so that is what I do now. As even with 30,000 items, the background thread happened in milliseconds. So thanks user Insomniac Geek.

In real production code, you would not do this, you would more likely do something like this:

Func<Dictionary<String, Object>, ThreadableItem<List<Client>>> taskFunc = (inputParams) =>
{
    try
    {
        try
        {
            //Obtain a list of Clients that work in particular BusinessArea 
            Service<IGateway>.Use((client) =>
            {
                RetrieveDataByQueryRequest request = new RetrieveDataByQueryRequest();
                request.Query = new Query().SelectAll(BusinessEntityType.Client)
                    .Where(new Filter("BusinessAreaId", 
                        FieldOpeartor.Equals,(Int32)inputParams["businessArea"]);

                RetrieveDataByQueryResponse response = 
                    (RetrieveDataByQueryResponse)client.Execute(request);

                return new ThreadableItem<List<Client>>(response.Clients, String.Empty);
            });
        }
        //catch WCF FaultExceptions
        catch (FaultException<SerializationException> sex)
        {
            //throw actual Exception which is caught it outer catch
            throw new BusinessException("A serialization issue has occurred");
        }
        //catch WCF FaultExceptions
        catch (FaultException<InvalidArgumentException> iaex)
        {
            //throw actual Exception which is caught it outer catch
            throw new BusinessException("One of the arguments is invalid");
        }

    }
    //something went wrong so return a new ThreadableItem with the failed
    //message in it
    catch(Exception ex)
    {
        return new ThreadableItem<List<Client>>(null, ex.Message);
    }
};

//setup Threading task function
threadVM.TaskFunc = taskFunc;

Adorners

Now that you know there is a ThreadableItemViewModel<T> property exposed that makes use of an internal ThreadableItem<T>, we can imagine making use of that in the UI. This is done via exposing a ThreadableItemViewModel<T> property in your own ViewModel, like this:

public ThreadableItemViewModel<List<StuffData>> ThreadVM
{
    get { return threadVM; }
}

In this case, the generic T is obviously a List<StuffData>. So how do we then use this in the actual View? Well, let's have a look, shall we?

<local:ThreadableHostControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
    ThreadableItem="{Binding ThreadVM}">

    <ListView Background="WhiteSmoke" BorderBrush="Black" BorderThickness="5"
              ItemsSource="{Binding ThreadVM.Data.DataObject}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Text" Width="500" 
                                DisplayMemberBinding="{Binding Path=Text}"/>
                <GridViewColumn Header="Age" Width="100" 
                                DisplayMemberBinding="{Binding Path=Age}"/>
            </GridView>
        </ListView.View>
    </ListView>
    
</local:ThreadableHostControl>

It can be seen that there is a ThreadableHostControl UserControl which makes use of this ThreadableItemViewModel<List<StuffData>> ThreadVM property exposed by the ViewModel.

There are two clever things going on here:

Let Content Be Content

In WPF, there are loads of different ways to do things, but I always like to keep my XAML as clean as I can. Part of the trick is understanding the framework, but also knowing what classes support what properties. A UserControl has a Content property, so let's just use that to host the data that we want to show which is fetched in the background thread. In this case, this will be a List<StuffData> bound to a ListView.

We could have done some tricks with several UI controls having their Visibility properties toggled, but that means more XAML, ouch. A better way is to let the content be content, and then we can host extra content on top of the content in a layer called the AdornerLayer.

If you do not know about the AdornerLayer, you should read this MSDN link: http://msdn.microsoft.com/en-us/library/ms743737.aspx.

Supporting Adorners

The ThreadableHostControl.ThreadableItem property is bound to an instance of ThreadableItemViewModel<T> which inherits from ThreadableItemViewModelBase, which as I mentioned earlier was the base class for all ThreadableItemViewModel<T> that supported a couple of properties namely:

  • IsBusy
  • Failed
  • ErrorMessage

So how does the ThreadableHostControl UserControl make use of the ThreadableItemViewModelBase property that it gets via a Binding? Well, that is quite interesting; let's have a look, shall we?

The ThreadableHostControls ThreadableItem DependencyProperty changed event calls an internal method called SetupPropertyWatcher():

private static void OnThreadableItemChanged(DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != null)
    {
        ((ThreadableHostControl)d).SetupPropertyWatcher(
            (ThreadableItemViewModelBase)e.NewValue);
    }
}

So looking into the SetupPropertyWatcher() method, we can see that it sets up the INotifyPropertyChanged property watchers for various properties of the ThreadableItemViewModelBase DP instance:

/// <summary>
/// Watches the IsBusy/Failed INPC properties of the 
/// ThreadableItemViewModelBase and when they change, 
/// swaps in/out the correct Adorner to suit the current 
/// state of the ThreadableItemViewModelBase
/// </summary>
/// <param name="item">The ThreadableItemViewModelBase to 
/// watch INPC changes on</param>
private void SetupPropertyWatcher(ThreadableItemViewModelBase item)
{
    if (threadableItemObserver != null)
    {
        threadableItemObserver.UnregisterHandler(n => n.IsBusy);
        threadableItemObserver.UnregisterHandler(n => n.Failed);
    }
    threadableItemObserver = new PropertyObserver<ThreadableItemViewModelBase>(item);
    threadableItemObserver.RegisterHandler(n => n.IsBusy, this.IsBusyChanged);
    threadableItemObserver.RegisterHandler(n => n.Failed, this.FailedChanged);
}

Note: I am making use of Josh Smith's most excellent PropertyObserver to rig up watcher methods on the the original INPC object.

If we follow one of these through, say the Failed -> FailedChanged() method, we will see what happens:

/// <summary>
/// Shows the FailedAdorner
/// </summary>
private void FailedChanged(ThreadableItemViewModelBase vm)
{
    if (vm.IsBusy)
        return;

    //If the users chose to throw an Exception on a null AdornerLayer
    //throw an Exception. The user may change this setting in the App.Config
    //which obviously impacts how the code works, but the Background
    //thread will still run, its just that the Adorners that this class
    //manages will not be shown. Which is not how the code was intended
    //to work. It would be better to find out why the AdornerLayer is null
    adornerLayer = AdornerLayer.GetAdornerLayer(this);

    if (shouldThrowExceptionOnNullAdornerLayer && adornerLayer == null)
        throw new NotSupportedException(
            "The ThreadableHostControl will only work correctly\r\n" +
            "if there is an AdornerLayer found and it is not null");

    if (adornerLayer != null)
    {
        if (vm.Failed)
        {
            SafeRemoveAll(new List<CustomAdornerBase>() { failedAdorner, busyAdorner });
            failedAdorner = new FailedAdorner(this, vm.ErrorMessage);
            adornerLayer.Add(failedAdorner);
        }
        else
        {
            SafeRemoveAll(new List<CustomAdornerBase>() { failedAdorner, busyAdorner });
        }
    }
    //repaint
    InvalidateControl();
}

See how this method is used to show a FailedAdorner in the AdornerLayer (if it's found and not null). The last piece of the puzzle is to see what the actual FailedAdorner looks like. Well, it's dead simple (there is a CustomAdornerBase base class, but I'll spare you that); it looks like this:

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Collections.ObjectModel;

namespace ThreadingComponent
{
    /// <summary>
    /// Failed adorner that is shown when the threading 
    /// operation fails
    /// </summary>
    public class FailedAdorner : CustomAdornerBase, IResizableAdornerControl
    {
        #region Data
        private FailedUserControl failedUserControl;
        #endregion

        #region Constructor

        public FailedAdorner(FrameworkElement adornedCtrl, String text)
            : base(adornedCtrl)
        {
            failedUserControl = new FailedUserControl();
            failedUserControl.ErrorMessage = text;
            failedUserControl.Margin = new Thickness(0);
            host.Children.Add(failedUserControl);
        }

        #endregion 

        #region IResizableAdornerControl
        public void ResizeToFillAvailableSpace(Size availableSize)
        {
            host.Width = availableSize.Width - 5;
            host.Height = availableSize.Height - 5;
            failedUserControl.ResizeToFillAvailableSpace(availableSize);
        }
        #endregion
    }
}

All it does is host a FailedUserControl in the AdornerLayer. So the last step is to see what the FailedUserControl looks like. Well, here is its XAML:

<UserControl x:Class="ThreadingComponent.FailedUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="Auto" Width="Auto" HorizontalAlignment="Stretch" 
             VerticalAlignment="Stretch">
    <Grid Margin="0,3,0,0" HorizontalAlignment="Stretch" 
          VerticalAlignment="Stretch" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" 
               HorizontalAlignment="Stretch" 
               Background="Black">
            <Image Source="../Images/Failed.png" 
               Height="45" Margin="5"/>
            <Label FontFamily="Arial" FontSize="24" 
               FontWeight="Bold" Content="Failed" 
               HorizontalAlignment="Left" 
               HorizontalContentAlignment="Left" 
               Foreground="White"
               VerticalAlignment="Center" 
               VerticalContentAlignment="Center"/>
        </StackPanel>


        <TextBox Grid.Row="1" 
             TextWrapping="Wrap" BorderThickness="0"
             BorderBrush="Transparent" Background="Transparent"
             IsReadOnly="True" FontSize="16" 
             FontWeight="Bold" 
             HorizontalAlignment="Stretch" 
             VerticalAlignment="Stretch"
             Text="The following error occurred whilst trying to obtain the data:"/>

        <TextBox Grid.Row="2" TextWrapping="Wrap" 
             BorderThickness="0" Margin="0,10,0,0"
             BorderBrush="Transparent" 
             Background="Transparent"
             IsReadOnly="True"
             HorizontalAlignment="Stretch" 
             VerticalAlignment="Stretch"
             Text="{Binding Path=ErrorMessage}"/>
    </Grid>

</UserControl>

It can be seen that the FailedUserControl uses the ErrorMessage property to display the error that occurred with the threading operation. This ErrorMessage property DP on the FailedUserControl was set on the FailedUserControl by the FailedAdorner in response to the actual ThreadableItemViewModelBase.Failed property changing.

Here is the code-behind for the FailedUserControl:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ThreadingComponent
{
    /// <summary>
    /// Control that is shown when the threading 
    /// operation fails
    /// </summary>
    public partial class FailedUserControl : UserControl, IResizableAdornerControl
    {
        #region Constructor
        public FailedUserControl()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        #endregion

        #region DPs
        #region ErrorMessage

        /// <summary>
        /// ErrorMessage Dependency Property
        /// </summary>
        public static readonly DependencyProperty ErrorMessageProperty =
            DependencyProperty.Register("ErrorMessage", 
            typeof(String), typeof(FailedUserControl),
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Gets or sets the ErrorMessage property.
        /// </summary>
        public String ErrorMessage
        {
            get { return (String)GetValue(ErrorMessageProperty); }
            set { SetValue(ErrorMessageProperty, value); }
        }

        #endregion
        #endregion

        #region IResizableAdornerControl
        public void ResizeToFillAvailableSpace(System.Windows.Size availableSize)
        {
            this.Height = availableSize.Height;
            this.Width = availableSize.Width;
        }
        #endregion
    }
}

Note: ThreadableItemViewModelBase.IsBusy works in the same way as this, except it has a BusyAdorner and a BusyUserControl.

Configuration

As I have stated in various places in the article, the attached demo code's ThreadableHostControl UserControl uses the AdornerLayer, which can occasionally come back as null (say, if you are using your own AdornerDecorator, or are in the middle of some crazy control such as an Infragistics one or a fat Ribbon), and as such, the attached demo code's ThreadableHostControl UserControl would not really work as planned.

To deal with this, the user can choose how this can be handled by using the App.Config setting "shouldThrowExceptionOnNullAdornerLayer" which directs the app to throw an Exception if the AdornerLayer can not be obtained.

If the user chooses to set the "shouldThrowExceptionOnNullAdornerLayer" App.Config value to "false", the threading should all work as expected; it is just the busy or failed Adorners that will not be shown, and the user will have to work out another way of dealing with the IsBusy and Failed states of the background threading operation.

Obviously, it would be better for everyone if this setting remains set to "true" and the user finds out why the AdornerLayer.GetAdornerLayer(this) is returning null.

How to Go About Using This Idea in Your Own WPF App

All you really have to do to use all this in your own app is:

  1. Copy the images to your Images/ folder; if you store images somewhere else, you will need to modify the FailedUserControl and BusyUserControl.
  2. Copy the following files to your own app:
    • BusyAdorner.cs
    • CustomAdornerBase.cs
    • FailedAdorner.cs
    • IResizableAdorner.cs
    • SimpleCommand.cs (if you want to use ICommands)
    • BusyUserControl.xaml/cs
    • CircularProgressBar.xaml/cs
    • FailedUserControl.xaml/cs
    • ThreadableHostControl.xaml/cs
    • Images folder: Put these where you like, but see Step 1
    • BackgroundTaskManager.cs
    • ThreadableItem.cs
    • PropertyObserver.cs
    • ThreadableItemViewModel.cs
    • ThreadableItemViewModelBase.cs
    • ViewModelBase.cs
    • You can look at Window1ViewModel as a base, but you should create your own ViewModel to suit your needs
    • App.config must contain the AppSettings key "ShouldThrowExceptionOnNullAdornerLayer"
  3. Create the right type of ThreadableItemViewModel<T> and expose that as a property from your ViewModel that drives your view, where T is nailed down to some Type
  4. Create the correct ThreadableItemViewModel<T>.TaskFunc property value which is expected to be of type Func<Dictionary<String,Object>,ThreadableItem<T>>

Limitations

There is no support for cancelling a threading work item delegate.

That's It

That is all I wanted to say right now. I have to say, for me, this was a real problem on a large scale WPF app, and this code will solve very real issues we have on that project. If you too feel that this could be useful in your WPF app, could you spare the time to make a comment or a vote? Many thanks.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberDean Oliver31-Jan-12 2:31 
Finally an article thats based on the real world. Most articles tell you how to do things but they don't base it on how it should be done in real world applications. great work
GeneralMy vote of 5memberlionheart_120511-Nov-10 4:54 
have performane issues in my wpf app but still wanna have a responsive ui
GeneralGreat Work as Usual... Need help [modified]memberbhuvan0120-Sep-10 4:24 
First this is a great article...
I have this Modal Form which is used to run some long running operation. Once the operation is completed, I need to either close the form or hide and navigate to another modal form. Is there a way that I can get notified of the task completion?
 
Thanks
bp
 

 
-- Modified Monday, September 20, 2010 10:52 AM
GeneralSuggestion. AsyncCommand instead of ThreadingViewModelmemberMember 342384717-Feb-10 10:35 
Sacha,
 
Thanks for an excellent article. I actually solved this problem a little while ago. I wanted to show everyone this alternate solution. If you have time, please provide feedback.
 
As the subject suggests, I created an AsyncRelayCommand instead of creating a ThreadableItemViewModelBase like you did. Rest of the logic is pretty much the same. Each AsyncRelayCommand has a backgroundworker in it.
 
There are two cases where I see that I need to do threading.
 
1) Database queries
2) User interaction that starts a potentially wrong process. This could also involve database calls.
 
For all these things, I am using these commands.
 
Thanks,
 
Nachiket
GeneralRe: Suggestion. AsyncCommand instead of ThreadingViewModelmvpSacha Barber17-Feb-10 19:59 
Sounds sensible indeed, perhaps you could share a link to your work
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Suggestion. AsyncCommand instead of ThreadingViewModelmemberMember 342384718-Feb-10 4:42 
Here is the code. There really isn't much in it.
 
public class AsyncRelayCommand : RelayCommand, IDisposable
    {
        BackgroundWorker worker = new BackgroundWorker();
 
        public Window ProgressWindow { get; set; }
 
        #region Contructors
            
        
        // Creates a new command that can always execute.
        // param name="execute" The execution logic.
        public AsyncRelayCommand(Action<object> execute) : base(execute) 
        {
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }
 
        
        /// Creates a new command.
        
        /// param name="execute" The execution logic
        /// param name="canExecute" The execution status logic
        public AsyncRelayCommand(Action<object> execute, Predicate<object> canExecute) : base(execute, canExecute) { }
 
        #endregion
 

        public override void Execute(object parameter)
        {
            while (worker.IsBusy)
            {
                //wait
            }
            worker.RunWorkerAsync(parameter);
            
            if (ProgressWindow != null)
            {
                ProgressWindow.Show();
            }
        
        }
 

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.toExecute(e.Argument);
        }
 
        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (ProgressWindow != null)
            {
                ProgressWindow.Hide();
            }
        }
    }
}

GeneralRe: Suggestion. AsyncCommand instead of ThreadingViewModelmvpSacha Barber18-Feb-10 5:32 
I do not get this, where is toExecute() method shown above, should that not be your Action<Object> input parameter to AsyncRelayCommand, and also this is WPF, and I guess you are using AsyncRelayCommand to do nice MVVM apps right?
 
So doesn't having a Window right there in the command kind of break MVVM, and Unit Testability for that matter? How do you UnitTest that?
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Suggestion. AsyncCommand instead of ThreadingViewModelmemberMember 342384718-Feb-10 5:39 
toExecute is the Action<object> input parameter. My AsyncRelayCommand inherits from RelayCommand which is a bare bones implementation of ICommand.
 
The constructor public AsyncRelayCommand(Action<object> execute) : base(execute) calls the base which assigns toExecute that is given by the viewmodel.
 
As for your other comment about the ProgressWindow, you are right. I did not think about putting a view in this command. When an AsycnRelayCommand is executing, I need to disable the view with a progress window. What would you suggest for something like this?
 
Thanks.
GeneralRe: Suggestion. AsyncCommand instead of ThreadingViewModelmvpSacha Barber18-Feb-10 5:54 
What we tend to do is use IOC to inject either a real IProgressWindow, or some Mock IProgressWindow, which can be used it testing.
 
So the IProgressWindow may look like
 
public interface IProgressWindow
{
    Show()
    Hide();
}
 
And since you already have a Window that implements these, you can just have a Window that inherits from IProgressWindow, and inject that in your real code. Then for your tests, have a dummy IProgressWindow type that would be something like
 
public DummyProgressWindow : IProgressWindow
{
   public void Show()
   {
     //Do nothing
   }
 
   public void Hide()
   {
     //Do nothing
   }
 
}
 
If you do not know about IOC, I would start with maybe either using Unity http://msdn.microsoft.com/en-us/library/cc511654.aspx[^] or Castle Windsor http://www.castleproject.org/container/documentation/index.html[^] or MEF http://www.codeplex.com/MEF[^]
 
or whatever IOC container you like.
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Suggestion. AsyncCommand instead of ThreadingViewModelmemberMember 342384718-Feb-10 8:14 
Makes sense. I think having AsyncViewModel is a good solution for this like you have here. Thanks.
GeneralName attribute on sub-controls issuememberiampud4-Jan-10 10:36 
First off great article. Wanted to test it out so put it on a page with a long running process and got the following:
 
Cannot set Name attribute value 'grid' on element 'GridControl'. 'GridControl' is under the scope of element 'ThreadableHostControl', which already had a name registered when it was defined in another scope.
 
I would remove the names on the sub-controls but they are use for data triggers and things of that nature so I moved the sub-controls to a static DataTemplate and it was happy.
 
Thanks again for a great article
GeneralRe: Name attribute on sub-controls issuemvpSacha Barber4-Jan-10 21:37 
Yeah this is not a limitation of this article as such it is just how WPF works, when you have UserControls inside DataTemplates, and the namescope comes and bites your ass. Solution is either what you did, or use lookless controls
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Name attribute on sub-controls issuememberRichard E King5-Jan-10 5:01 
Hey man read this blog post for more information : http://blog.bluecog.co.nz/archives/2007/08/27/wpf-cannot-set-name-attribute/[^]
 
Richard

GeneralRe: Name attribute on sub-controls issuemvpSacha Barber17-Feb-10 20:00 
Good work Richard, thanks for sharing that
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralExcellent SachamemberMarcelo Ricardo de Oliveira4-Jan-10 10:30 
Have 5 (again!) I like the way you put the problem at the beginning, and how you solve it... yes, many applications put everything on the UI thread, and it sucks because the user doesn't know what's going on. And one more annoying thing about the message boxes is that you can't copy their content (well, actually some have this option, but as you already said, what if the user close the message box?). So you took your time to create a very useful component for WPF to solve this. Thanks for sharing your ideas again Smile | :)
 
Just a little suggestion: in the demo you use a simple button ("Simulate Failure") to toggle the ShouldFail flag, but the user doesn't see if the flag is on or off. Wouldn't be cool to add a checkbox for the toggle state or something like that?
 
Take a look at full source code C# Snooker game here in Code Project.

GeneralRe: Excellent SachamvpSacha Barber4-Jan-10 21:35 
Marcelo Ricardo de Oliveira wrote:
Just a little suggestion: in the demo you use a simple button ("Simulate Failure") to toggle the ShouldFail flag, but the user doesn't see if the flag is on or off. Wouldn't be cool to add a checkbox for the toggle state or something like that?

 
Very good point, Ill fix that as soon as possible.
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralI'm not sure I understandmemberDewey30-Dec-09 12:27 
Are you simply trying to communicate information from other parts of your application to the View?
GeneralRe: I'm not sure I understandmvpSacha Barber30-Dec-09 20:22 
Well kind of.
 
Basically this article creates a new thread does some work which you specify, but while its doing it the UI shows an IsBusy animation, then if during the threading operation something fails we show an Exception area in the ui, if all goes ok then we show the data fetched by the threading operation.
 
What do you not see the point of it all, I think its very useful and a nice generic approach that would otherwise require you to write lots of UI code, or even in PRISM you would have to load extra stuff into regions?
 
What don't you get?
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: I'm not sure I understandmemberDewey31-Dec-09 0:04 
I see the problem you're trying to solve now, and it makes sense.
 
My first thought was using something like MEF(or Unity), but that might get a tad messy.
 
My second thought was, why not use something like MVVM Light's messaging, which provides weak references to do the job. I think you could make that work, and it's ability to send types makes it really robust and elegant.
 
However, as I said, I wasn't completely sure of what your gaol was, and your statement in this post was clearer to me than the intro to the article(which is absolutely my problem, sorry).
 
Having said all of that, I do think you have one solid solution to the problem, even though I still think the messaging solution would fit my code a bit better, which is mostly Silverlight these days.
 
Either way, good work!
GeneralRe: I'm not sure I understand [modified]mvpSacha Barber31-Dec-09 0:32 
Dewey
 
I have to say I think you are way off the trail man. Laurents VVM Light's messaging or Josh and Marlons Mediator or PRISM EventAggregator are just messaging systems.
 
They do nothing for background loading to keep the UI responsive. And what has MEF got to do with it OR Unity. And WeakReference for that matter. WeakReference is used by ALL those messaging systems to allow delegates to hook up to the messager while still allowing the source / target object to be garbage collected. Remember delegates hold reference to target. That is why WeakReferences are used.
 
MEF is addins, UNITY is an IOC container. I do not mean to sound harsh here, and would like to help you get what I am talking about, but I do not think you understand the issues at all. I want to load some data on a different thread, and show some status while the bakground thread is doing its job.
 
Laurents messager (which is good) but has nothing to do with threading, it can not do this job without writing extra threading code, and that is because its primary role is a messager NOT a threading helper. I think the issue is you are too used to SL, and I do not even know if you can spawn a background thread in SL, does it allow that. Maybe that is it, this sort of code is more common in WPF I guess.
 
I know you can use the async programming pattern in SL against WCF or ASMX web services or whatever, and async is also supported by RIA services, but you must see ALL of these have nothing to do with what this article talks about.
 

To be clear, Laurent (or any other messager) sends a message with some state object maybe, which is seen by some recipient of the message, now this could be new data, but what if that needs to happen in a background thread (which as I say does SL even support, can it create new threads arbitrarily, or must is use the asycn pattern), then you need to make sure UI bindings are updated on correct thread, or you WILL get cross threading Exceptions. And that is without even showing busy/failure statuses, that is just getting the new data for a property on your VM.
 
Maybe if you were to talk in a bit more detail about how you would do the same as this, using MEF/Unity and Messager, we could see if you have a viable solution.
 
Again do not mean to sound harsh, its just a discussion.
 
Smile | :)
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net
modified on Thursday, December 31, 2009 6:38 AM

GeneralRe: I'm not sure I understandmemberDewey31-Dec-09 8:32 
Actually, this is simple using MVVM, and yes, you can spin off as many threads as you want in SL, just like WPF, no difference. BTW, the messaging would be useless if it wasn't threaded! I believe(not 100% sure) that the thread used will be based on the current thread you send the message from.
 
However, I could send a message anywhere in my app(loaded or dynamically loaded), with as much data as required, process that data, and fire off progress messages, error reports, and basically anything I want to the UI for updating... without having to manage any threading at all.
 
You have fixated on MEF/Unity, solutions that I told you I rejected, however, using an IOC is not out of the question at all. A nice feature of those containers is that they could provide you a reference to an already instantiated class to an unrelated section of your code.
 
I admit that I learned WPF from using SL, and while there are some disadvantages, there are huge advantages. I don't know what I'm missing(feature wise), so I have to build it. The biggest advantage is that my UI's cross both platforms, where as WPF designs have trouble moving the other way(they're generally higher fidelity XAML, so to speak).
 
And, yes, you did sound a bit harsh, but that's fair if you think someone is way off base. I just think there is another way, which may or may not be better, just different.
GeneralRe: I'm not sure I understandmvpSacha Barber31-Dec-09 11:10 
Dewey,
 
Still not sure about your whole Messager debate here, for example try and create a new thread in one of your message delegates and then send the result, I was not aware that Laurent did any Dispatcher.Invoke (but I will ask him) on messager send.
 
Send me an example where you spin a new thread, If I am arong totally humble apologies from me, its just I do not feel I am. remember I have a weakReference messaging solution in my CInch framework.
 
Like I say send me an email with your solution using Laurents MVVM messager, and I will point him to this post (he is the same group as me, so I can ask him directly).
 
I still feel using an IOC container is way off, as its an instance for an interface, so what happens when you want that interface instantiated to do something different, you are just getting it from an IOC container, not threading the results, dont see what you mean to be honest.
 
Again, I really am not trying to be an arse here, I just want to understand what you are talking about.
 
I will be checking with Laurent for sure.
 
I am sure Laruent used my own Disptatchr.InvokeIfRequired() extension methods, so we shall see about that one
 
Do me a favour post a message on one of my blog posts and I will get back to you with my proper email, and you and I can exchange ideas and code when I have your correct email.
 
Many thanks
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: I'm not sure I understandmvpSacha Barber31-Dec-09 11:15 
Just one point,
 
Dewey wrote:
I believe(not 100% sure) that the thread used will be based on the current thread you send the message from

 
If you create a new thread o do background work, that WILL not be THE THREAD that the UI is bound to. Thats the problem.
 
Honestly stick with me, and lets bash this one out, I would love to see what you mean.
 
Dewey wrote:
owever, I could send a message anywhere in my app(loaded or dynamically loaded), with as much data as required, process that data, and fire off progress messages, error reports, and basically anything I want to the UI for updating... without having to manage any threading at all.

 
This would ALL be done in the UI thread unless a new thread was spawned, again I will check with Laurent on this. And maybe get him involevd with this discussion, as it is important I think, as you are using his component.
 
And I have to say though I have knowledge of his old codebase, he may have changed it, but his old code base was certainly synchronous in nature.
 
Get hold of me as suggested below and lets sort this one out.
 
You know no bad feeling at all, its all cool, I just want us both to understand is all.
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: I'm not sure I understandmvpSacha Barber2-Jan-10 0:47 
Dewey I have contacted Laurent about this discussion, and it is as I stated, Laurents messager does not dispatch at all. I have asked Laurent to post his 2 cents on this forum which he said he would once he is back from holiday.
 
So I think between us we will get you the answer you need (well seems I need it more than you, but there you go).
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: I'm not sure I understandmemberDewey2-Jan-10 20:29 
Sacha, I tested Laurent's code, and was a bit dissappointed that he doesn't really provide messaging as I understand it. his library is more of a method calling utility, not real messaging.
 
However, it still presents no problem in producing a solution that works. His code will be on whatever thread you call it from, so what I did was create two seperate controls(in SL). One called a publisher, the other a subscriber(I'm reusing some prism event aggregation code). It works like this.
 
1. In the Publisher, I send a message to the subscriber to start a long running process.
2. The subscriber recieves the message, and starts the process, reports progress inside of it's thread, sending it back to the publisher.
3. The publisher updates his screen to see the progress in a progress control.
 
Note that I could send any data I want to the publisher from the long running process that I many choose, progress, complex type data, etc.
 
This exercise proved to me that Prism or MEF can solve the same problem, and here's why.
 
Microsoft is in the process of delivering differing sets of technology based on a single base concept, some form of type catalog. MEF is based on a type catalog, and so is Unity. Interesting, MVVM Light is a special case of this type catalog paradigm. It keeps a type catelog of messages to facilitate routing.
 
Therefore, I could achieve the same result from any system implementing type catalogs, because all I'm looking for is a mapping to get me from one place in my code to another. BTW, two points on Laurents code. First, the system we use his code in masked the fact that it isn't threaded, because we use a tight threaded message loop to dispatch. Second, we already had a type catalog style system in place, but it wasn't threaded, and someone believed that his was(probably from the name). I still like it because it truly does more than the primitive method calling we did quick and dirty.
 
Finally, I also discovered that neither is required to get information from a long running process because the BackgroundWorker class supports running a long process and providing progress information. I'm not sure if you can send complex data back(I didn't really check), but I did get it working in SL.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130617.1 | Last Updated 30 Dec 2009
Article Copyright 2009 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid