Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Asynchronus MVVM... Stop the Dreaded Dead GUI Problem in WPF

0.00/5 (No votes)
9 Nov 2010 2  
Shows how to implement an asynchronous Model in the MVVM pattern.

Introduction

Many a developer has found the dreaded GUI freeze problem simply by calling a method which takes a long time to execute. The reason for this is that the "long outstanding job" will execute on the same Thread as the GUI program. This stops all events from being posted and handled, thus locking up the User Interface and rendering the application unusable until the background job is complete! Not a very professional way to deliver a product indeed.

This site has many articles related to this topic, many of them are very good. Some are complicated, other require knowledge of the internals regarding the solution. This solution offers something a bit different. It takes an older technology, BackGroundWorkers), and adds in the ICommandinterface, a Delegate here, and an Event there, and we have a fully asynchronous and easy solution to this common issue. Perhaps the best part of this is that you can take this project and easily change it to suit your needs without knowing too many details.

Using the code

Simply download the code and run it in Debug mode to see it work. This is the main screen:

AsychronousMainWindow.png

The top row of buttons are the TabItem in the Main TabControl You'll default to the first tab which has two buttons: "Start the Asynchronous Job" and "Stop the Asynchronous Job". Once you start the job, you will see a progress bar at the bottom begin to show the status of the work. Press the "Stop" button and the background work will stop.

To see the impact of the BackGroundWorkeron the main GUI thread, just press any of the other tabs while the progress bar is working. You can browser the web, or you can view the code diagrams of how this was designed.

Points of Interest

The BaseCommand Class

So what is the BaseCommandall about? It is a BackgroundWorkerthat implements the ICommandinterface. Agile programming techniques teach us to abstract away the commonality. The BaseCommandclass is an attempt to do that.

public class BaseCommand :  BackgroundWorker, ICommand
{
    public bool canexecute = true;
    public event EventHandler CanExecuteChanged;

    //------------------------------------------------------------------
    public BaseCommand()
    {
        this.WorkerSupportsCancellation = true;
        this.WorkerReportsProgress = true;
        this.DoWork += new DoWorkEventHandler(BWDoWork);
        this.ProgressChanged += 
          new ProgressChangedEventHandler(BWProgressChanged);
        this.RunWorkerCompleted += 
          new RunWorkerCompletedEventHandler(BWRunWorkerCompleted);
    }

    //------------------------------------------------------------------
    public virtual void BWRunWorkerCompleted(object sender, 
                        RunWorkerCompletedEventArgs e)
    {
    }

    //------------------------------------------------------------------
    public virtual void BWProgressChanged(object sender, 
                        ProgressChangedEventArgs e)
    {
    }

    //------------------------------------------------------------------
    public virtual void BWDoWork(object sender, DoWorkEventArgs e)
    {
    }

    //------------------------------------------------------------------
    public virtual bool CanExecute(object parameter)
    {
        return true;
    }


    //------------------------------------------------------------------
    public virtual void Execute(object parameter)
    {
    }
}

As a BackgroundWorker we find that some fundamental settings are set in the base class. This worker does support cancellation, it reports progress, and the events for doing the work. It pre-wires the ProgressChangedevent handler as well as the RunWorkerCompletedevent handler. Note however, that the methods themselves are virtual This allows the concrete implementation to override these methods and implement what is desired.

The ICommandinterface is the virtualCanExecutemethod that defaults to trueand the Executemethod (also virtual, both meant to be overridden in the concrete class. One other small note, there is a var named (lowercase) canexecute We'll discuss this a bit later.

The Concrete BaseCommand Class Named AsychronousCommand

Inheriting the BaseCommandclass as shown below, the concrete class first defines two delegates. One is for ProgressChangedand takes a parameter of type intthat represents the progress (in percentage). The other delegate is the DataReadysignature which, in this case, takes an ObservableCollectionof type string This is the preliminary set up for allowing any registered listeners to receive "feedback". Finally, there are two events built on those delegates which will be used for the "loosely coupled" communication in the View Model.

public class AsynchronusCommand : BaseCommand
{
    <summary>
    /// This is the delegate definition to post progress back to caller via the 
    /// event named EHProgressChanged
    ///<summary>
    ///<param name="progress">
    // Hold the progress integer value (from 0-100)</param>
    //-----------------------------------------------------------------------
    public delegate void DlgProgressChanged(int progress);

    //-----------------------------------------------------------------------
    /// <summary>
    /// This is the delegate definition to post
    ///   a ObservableCollection back to the caller via the
    ///   event EHDataReady
    /// </summary>
    /// <param name="data">The signature
    ///   needed for the callback method</param>
    public delegate void DlgDataReady(ObservableCollection<string> data);

    //-----------------------------------------------------------------------
    //Static event allows for wiring up to event before class is instanciated
    /// <summary>
    /// This is the Event others can subscribe to,
    /// to get the post back of Progress Changed
    /// </summary>
    public static event DlgProgressChanged EHProgressChanged;

    //-----------------------------------------------------------------------
    //Static event to wire up to prior to class instanciation
    /// <summary>
    /// This is the event of which others can
    /// subscribe to receive the data when it's ready
    /// </summary>
    public static event DlgDataReady EHDataReady; 

    //-----------------------------------------------------------------------
    /// <summary>
    /// The Entry point for a WPF Command implementation
    /// </summary>
    /// <param name="parameter">Any parameter
    /// passed in by the Commanding Architecture</param>
    public override void Execute(object parameter)
    {
        if (parameter.ToString() == "CancelJob") 
        { 
            // This is a flag that the "other thread" sees and supports
            this.CancelAsync(); 
            return;
        }
        canexecute = false; 
        this.RunWorkerAsync(GetBackGroundWorkerHelper());
    }

So now we arrive to the ICommandpart of this class. This is the part of the class that will receive notification from a bound Command in WPF via the Executemethod (shown above). WPF calls the Executemethod via the XAML command binding in this demo. The first check in the method is to see if the parameter passed in was to Cancel the Job; which, is just an arbitrary string of "CancelJob". If so, this Thread is Canceled via the code shown, and control is returned to the application. If this is not a Cancellation, then the code invokes the asynchronous RunWorkerAsyncmethod (the built-in support from the BackgroundWorkerclass). But wait a second, what's this GetBackGroundWorkerHelper()call all about?

BackGroundWorkerHelper

I found out long ago that communicating to other threads was vastly simplified by creating a BackGroundWorkerHelperclass. It is nothing more than a container for anything you want to pass in and out of another thread. In this sample below, we show two objects and a SleepIterationvalue being set. This is in the instantiation of the BackGroundWorkerHelper The BackGroundWorkerHelperdoes have other variables in it we will see later.

//-----------------------------------------------------------------------
/// <summary>
/// A helper class that allow one to encapsulate everything
/// needed to pass into and out of the 
/// Worker thread
/// </summary>
/// <returns>BackGroundWorkerHelper</returns>
public BGWH GetBackGroundWorkerHelper() 
{
    //The BGWH class can be anything one wants it to be..
    //all of the work performed in background thread can be stored here
    //additionally any cross thread communication can
    //be passed into that background thread too.
    BGWH bgwh = new BGWH(){obj1 = 1,
                           obj2 = 2,
                           SleepIteration = 200};
    return bgwh;
}

So what does the BackGroundWorkerHelperclass look like? For this demo we arbitrarily set it up as shown below. The objects don't do anything other than to show you that anything may be passed to the other thread. Notice that the var Datais the ObservableCollectionthat will be populated in the background thread.

//////////////////////////////////////////////////////////////////
/// <summary>
/// Background worker Class allows you to pass in as many objects as desired,
/// Just change this class to suit your needs. 
/// </summary>
public class BGWH 
{
    /// <summary>
    /// This demo chose a Object for the first "thing" to be passed in an out.
    /// </summary>
    public object obj1;  

    /// <summary>
    /// This is the second thing and shows another "object"
    /// </summary>
    public object obj2;

    /// <summary>
    /// An arbitrary integer value named SleepIteration
    /// </summary>
    public int SleepIteration;

    /// <summary>
    /// An observable collection
    /// </summary>
    public ObservableCollection<string> Data = 
           new ObservableCollection<string>(); 
}

Knowing that this BackGroundWorkerHelperis nothing more than a convenience class, it then becomes very simple to pass in complex data structures, DataTable, List, and the whole lot... Remember though that you don't want to pass in references to GUI objects because you will never be able to update them from these threads without some special code. Besides, this violates the rules of MVVM in that the ViewModel handles the content.

Doing the Work Asynchronously

So where is the "other thread"? The BackGroundWorkerwill spin off an asynchronous thread via the RunWorkerAsyncmethod call discussed earlier. It will call the BWDoWorkmethod which we hooked up via an EventHandler registration in the BaseCommandclass. But notice here that we over-rode that method in the base class in the Concreteclass, as shown below.

//-----------------------------------------------------------------------
/// <summary>
/// This is the implementation of the Asynchronous logic
/// </summary>
/// <param name="sender">Caller of this method</param>
/// <param name="e">The DoWorkEvent Arguments</param>
public override void BWDoWork(object sender, 
                System.ComponentModel.DoWorkEventArgs e)
{
    //Ahh! Now we are running on a separate asynchronous thread
    BGWH bgwh = e.Argument as BGWH; 
    //always good to put in validation
    if (bgwh != null)
    {
        //we are able to simulate a long outstanding work item here
        Simulate.Work(ref bgwh, this);
    }
    //All the work is done make sure to store result
    e.Result = bgwh;
}

Notice also how easy it is to "unpack" the cargo. DoWorkEventArgscontains an argument which we know is a BackGroundWorkerHelper How do we know? Because we passed it in via the GetBackGroundWorkerHelper()method call we discussed earlier. We unpack this helper class, and then check for null and pass it in as a reference to the Simulate.Workmethod. All the Simulate.Workcall does is to enter a loop, wait a bit, add data to the ObservableCollectionin the BackGroundWorkerHelperclass, and get this... It posts a Progress Event notification all the way back to the View via the ViewModel... Let's take a look.

Simulate Work Loop

There's a loop that uses the BackGroundWorkerHelpers SleepIterationcount to control how many times to loop, then there's a call to report progress via the TheAsynchCommand.ReportProgresscall, as shown below.

 //-----------------------------------------------------------------------
public static void Work( ref Model.BGWH bgwh, BaseCommand TheAsynchCommand) 
{
    //shows how the BGWH can have many different control mechanisms
    //note that we pass it in as a reference which means all updates here are 
    //automatically reflected to any object that has this reference.
    int iteration = bgwh.SleepIteration;

    //This is iterative value to determine total progress. 
    double perIteration = .005;

    //simulate reading 200 records with a small delay in each..
    Random r = new Random();

    for (int i = 0; i < iteration + 1; i++)
    {
        System.Threading.Thread.Sleep(r.Next(250));

        //Update the data element in the BackGroundWorkerHelper
        bgwh.Data.Add("This would have been the " + 
                      "data from the SQL Query etc. " + i);

        //currentstate is used to report progress
        var currentState = new ObservableCollection<string>();
        currentState.Add("The Server is busy... Iteration " + i);
        double fraction = (perIteration) * i;
        double percent = fraction * 100;

        //here a call is made to report the progress to the other thread
        TheAsynchCommand.ReportProgress((int)percent, currentState);

        //did the user want to cancel this job? If so get out.
        if (TheAsynchCommand.CancellationPending == true)
        {
            // get out of dodge
            break;
        }
    }
}

Note that at the bottom of the loop, there is a check for Cancelling the work. If true we simply break out of this loop and exit the class. Because the BackGroundWorkerHelperhelper was passed as a reference, the data content is already set! It is ready to be posted back to the ViewModel.

Back to the AsynchronousCommand Class

Remember those event handlers we wired up and over-rode? When the Simulate.Workclass wanted to report progress, it simply fired the event to report progress. The event handler in the AsynchronousCommandclass picked it up (running on the GUI thread) and then fired an event to tell the View Model. This is shown below via the EHProgressChanged(progress)signal.

//-----------------------------------------------------------------------
/// BackGround Work Progress Changed, runs on this object's main thread
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public override void BWProgressChanged(object sender, 
       System.ComponentModel.ProgressChangedEventArgs e)
{
    //allow for a Synchronous update to the WPF Gui Layer
    int progress = e.ProgressPercentage;

    //notify others that the progress has increased.
    EHProgressChanged(progress);
    EHDataReady((ObservableCollection<string>)e.UserState);
}

The EHDataReadyis also signaled in an attempt to update the data before it was complete. Tests are inconclusive on whether or not data was able to be displayed on a ProgressNotifyoperation as shown. More work needs to be done there. However, as you will see in this application, the progress bar runs perfect.

One may ask, "How do we control the user from repeatedly kicking off the same command?" Remember that ubiquitous variable canexecutein the BaseCommandclass? The method below which is called when the BackGroundThreadis complete takes care of states.

/// <summary>
/// Handles the completion of the background worker thread.
/// This method is running on the current thread and
/// can be used to update the execute method with information as needed
/// </summary>
/// <param name="sender">The sender of this event</param>
/// <param name="e">The Run WorkerCompleted Event Args</param>
public override void BWRunWorkerCompleted(object sender, 
       System.ComponentModel.RunWorkerCompletedEventArgs e)
{
  //ideally this method would fire an event
  //to the view model to update the data
  BGWH bgwh = e.Result as BGWH;
  var data = bgwh.Data;
  //notify others that the data has changed.
  EHDataReady(data);
  EHProgressChanged(0);
  canexecute = true;
}

The Asynchronous Commands' BWRunWorkerCompetedevent handler, which was overridden from the BaseCommandclass (and was pre-wired to receive the event), receives the notification. It parses the BackGroundWorkerHelper and then fires two events, the first is the Dataevent (handled in the ViewModel), the other is to reset the ProgressBarto zero, and finally, canexecuteis set to true which will allow this command to be invoked again (canexecutestops multiple triggering of the same command). Note; however, this can also be accomplished in the Executemethod by checking to see if the thread is busy; if it is, the user should be notified via a message. The solution, as is, will not allow the user to press the button more than once, which is a nice approach.

The ViewModel

Just how did the ViewModel handle these events? Looking at the first two lines of code in the ViewModel, we see that two EventHandlers were wired up to the static Model's AsynchronousCommand delegates. Remember way back at the top of the article were we defined these signatures?

//-----------------------------------------------------------------------
public MainWindowViewModel()
{
    //the model will send a notification to us when the data is ready
    Model.AsynchronusCommand.EHDataReady += 
      new Model.AsynchronusCommand.DlgDataReady(
      AsynchronusCommandEhDataReady);
    //this is the event handler to update
    //the current state of the background thread
    Model.AsynchronusCommand.EHProgressChanged += 
      new Model.AsynchronusCommand.DlgProgressChanged(
      AsynchronusCommandEhProgressChanged);

And here are the methods in the ViewModel... Pretty simple really, as all they take is an integer for the progress in the first method, and an ObservableCollectionof string in the second.

///-----------------------------------------------------------------------
// The event handler for when there's new progress to report
void AsynchronusCommandEhProgressChanged(int progress)
{
    Progress = progress;
}

//-----------------------------------------------------------------------
// The event handler for when data is ready to show to the end user
void AsynchronusCommandEhDataReady(ObservableCollection<string> data)
{
    Data = data;
}

From there, they merely invoke the property setter as shown. WPF takes care of the rest, and we see something like this happen when all is said and done.

The End of the Demo

Additional Comment

This article demonstrates an easy way to spin off another thread to do work, while the GUI thread remains unaffected. It shows how a BackGroundWorkerand an ICommandinterface can be wired up from XAML, using the MVVM pattern. The AsynchronusCommand (if you didn't already notice) is in the folder named Model, and not by accident. What we have here is a new way a Model can be implemented. It is "Loosely Coupled", and it runs in another thread. It even, without having a reference to the ViewModel, updates the View via the fired Events in the Simulate.Workclass, which were handled by the ViewModel. Agile brought us to this point.

History

  • 11/01/2010 - Formatted code (JSOP).
  • 10/29/2010 - First edition.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here