Click here to Skip to main content
15,887,966 members
Articles / Desktop Programming / Windows Forms

A common class for executing tasks with a responsive UI

Rate me:
Please Sign up or sign in to vote.
4.74/5 (21 votes)
24 Jan 2009CPOL1 min read 93.4K   677   81   58
Execute actions while making the form wait and still be responsive to other tasks.

1.jpg

Introduction

This is a common way to implement the standard WinForms behavior of executing a potentially long running action while still having the UI be responsive and allowing the user to know that something is happening via the UI. This can be used with standard WinForms, WPF, or an MVP style application which is how I use it.

Background

Some of the other options which I felt weren't as easy as this to use:

Using the code

Download the source and hit F5 - three examples are given:

  • The standard behavior where the UI locks while executing.
  • The new behavior where the UI is not locked and the user can tell the form is busy.
  • What happens in the new behavior when an exception is thrown.

In order to use this, the form or view in question must implement the interface IThreadedExecuterView, in either the form itself or in a base form:

C#
public partial class Form1 : Form, IThreadedExecuterView
{

#region IThreadedExecuterView Members
public void SetWait(bool isEnabled)
{
     this.Cursor = (isEnabled ? Cursors.Default : Cursors.WaitCursor);
     button1.Enabled = button2.Enabled = isEnabled;
}

public void HandleException(Exception ex)
{
     MessageBox.Show("This is your standard error " + 
                     "handling call here for " + ex.Message);
}

Below is an example of it in use - there is no need to worry about the UI thread, creating delegates, or doing anything special for exceptions other than handling them in one place.

C#
using (ThreadedExecuter<BusinessObject> executer = 
         new ThreadedExecuter<BusinessObject>(this))
{
     executer
          .Process(() =>
          {
               return GetData(); //executes in background worker
          })
          .WhenFinished(businessObject =>
          {
               UseData(businessObject); //executes on UI thread
          })
     .Run();
}

It works for value types or reference types. Here is a Unit Test demonstrating its usage:

C#
[Test]
public void TestThreadedExecuterNormalBehavior()
{
     int result = 0;
     bool didComplete = false;
     AutoResetEvent waiter = new AutoResetEvent(false);
     IThreadedExecuterView view = 
        MockRepository.GenerateStub<IThreadedExecuterView>();
     using (ThreadedExecuter<int> worker = new ThreadedExecuter<int>(view))
     {
          worker
               .Process(()=>
               {
                    Thread.Sleep(1000);
                    return 42;
               })
               .WhenFinished(workerResult => 
               {
                    result = workerResult;
                    didComplete = true;
                    waiter.Set();
               })
          .Run();
     }

     waiter.WaitOne(2000, false);
     Assert.AreEqual(42, result);
     Assert.IsTrue(didComplete);
}

History

  • 24-Jan-2009 - Initial version.

License

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


Written By
United States United States
I've been a software developer since 1996 and have enjoyed C# since 2003. I have a Bachelor's degree in Computer Science and for some reason, a Master's degree in Business Administration. I currently do software development contracting/consulting.

Comments and Discussions

 
GeneralRe: MVP Pin
uffejz29-Jan-09 4:21
uffejz29-Jan-09 4:21 
GeneralRe: MVP Pin
uffejz19-Feb-09 0:41
uffejz19-Feb-09 0:41 
GeneralRe: MVP Pin
Paul B.19-Feb-09 14:08
Paul B.19-Feb-09 14:08 
GeneralRe: MVP Pin
uffejz23-Feb-09 21:52
uffejz23-Feb-09 21:52 
GeneralRe: MVP Pin
Paul B.12-Mar-09 16:26
Paul B.12-Mar-09 16:26 
GeneralRe: MVP [modified] Pin
uffejz15-Mar-09 1:44
uffejz15-Mar-09 1:44 
GeneralRe: MVP Pin
Paul B.15-Mar-09 3:47
Paul B.15-Mar-09 3:47 
GeneralRe: MVP [modified] Pin
uffejz15-Mar-09 4:27
uffejz15-Mar-09 4:27 
oki, I'm new to this seperating stuff, so sorry for all dumb questions. Blush | :O

So my model class should still have the timer then?

I'm not sure how I should create the presenter, should I use one presenter for all usercontrols?

I like to do something like this in the form.

private void Form1_Load(object sender, EventArgs e)
{
    this._servoPresenter = new ServoPresenter();
    this._servoModel = new ServoModel();

    this.ucServo1.WireUp(this._servoModel, this._servoPresenter, RemoteObjects.ServoMotor1);
    this.ucServo2.WireUp(this._servoModel, this._servoPresenter, RemoteObjects.ServoMotor2);
}


And in the usercontrol:

public void WireUp(IServoModel servoModel, IServoPresenter servoPresenter, ServoMotor servoMotor)
{
    this.ServoModel = servoModel;
    this.ServoPresenter = servoPresenter;
    this._servoMotor = servoMotor;

    this.ServoModel.AddServo(servoMotor, this);
}


And in the model or something..:

    private Dictionary<string, ServoMotor> _servoList = new Dictionary<string, ServoMotor>();
        private Dictionary<string, INotify> _notifyList = new Dictionary<string, INotify>();
'
        public ServoModel(int bufferCount)
        {
            this._bufferCount = bufferCount;
        }

        private int _bufferCount = 0;
        private int _events = 0;

        public delegate void BufferedUpdatedDelegate();
        public event BufferedUpdatedDelegate BufferedUpdated;

        private void ServoUpdated()
        {
            this._events++;

            //only raise every bufferCount events
            if (this._events % this._bufferCount == 0)
            {
                BufferedUpdated();
            }
        }

        public void AddServo(ServoMotor servo, INotify notifier) 
        {
            if (this._servoList.ContainsKey(servo.Name) == false)
            {
                servo.ServoUpdated += new ServoMotor.ServoUpdatedDelegate(ServoUpdated);
                this._servoList.Add(servo.Name, servo);
                this._notifyList.Add(servo.Name, notifier);
            }
        }


modified on Sunday, March 15, 2009 11:22 AM

GeneralRe: MVP Pin
Paul B.15-Mar-09 12:28
Paul B.15-Mar-09 12:28 
GeneralRe: MVP [modified] Pin
uffejz16-Mar-09 3:02
uffejz16-Mar-09 3:02 
GeneralRe: MVP Pin
uffejz11-Mar-09 22:58
uffejz11-Mar-09 22:58 
GeneralRe: MVP Pin
Paul B.12-Mar-09 3:11
Paul B.12-Mar-09 3:11 
GeneralRe: MVP Pin
uffejz12-Mar-09 3:25
uffejz12-Mar-09 3:25 
GeneralRe: MVP Pin
uffejz18-Mar-09 23:48
uffejz18-Mar-09 23:48 
GeneralRe: MVP Pin
uffejz19-Mar-09 0:37
uffejz19-Mar-09 0:37 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.