Asynchronous tasks on Winform

The problem

When you create winform applications, doing tasks in the background is essential to avoid user frustation. Unfortunatly, it could make you write a lot more code. Here are some utility methods that reduce the complexity of asynchronous calls within windows forms.

Within you base class

You probably have a common base class for all your UI components. Add these methods :

namespace TestWinForm
{
    public class BaseForm : Form
    {
        // Execute some code in async mode.
        // When it's done, it calls the nextStep delegate, eventually with
        // an exception catched during the main action.
        protected virtual void Async(Action action, Action nextStep)
        {
            new Thread(delegate()
            {
                Exception exception = null;
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    exception = ex;
                }
                ThreadSafe(() => { nextStep(exception ); });
            }).Start();
        }

        // This allows a sub class to easily run a method within
        // an UI thread without the need of creating multiple
        // delegate signatures for each method signatures
        protected virtual void ThreadSafe(MethodInvoker method)
        {
            if (InvokeRequired)
                Invoke(method);
            else
                method();
        }
    }
}

Within your UI classes

Now the only thing you need to do is to encapsulate the methode content with the Async() method :

namespace TestWinForm
{
    public partial class MainForm : BaseForm
    {
        public MainForm()
        {
            InitializeComponent();
        }

        // Here is the async trick :
        // - UI will NOT freeze,
        // - you can add beautifull animated gifs
        private void button1_Click(object sender, EventArgs e)
        {
            DateTime? result = null;
            Async(
                () =>
                {
                    result = GetComplexDate();
                },
                (ex) =>
                {
                    if (ex == null)
                        textBox1.Text = result.Value.ToShortDateString();
                    else
                        textBox1.Text = ex.Message;
                }
            );
        }

        // This is the slow, data-intensive task :p
        private DateTime? GetComplexDate()
        {
            Thread.Sleep(3000);
            return DateTime.Now;
        }
    }
}

The traditional way

Just in case you didn’t get it. This is what you should NOT DO:

        // This was traditional way :
        // - UI will freeze until during 3 second ...
        // - you users will complain
        // avoid this !
        private void button1_Click2(object sender, EventArgs e)
        {
            try
            {
                textBox1.Text = GetComplexDate().ToShortDateString();
            }
            catch (Exception ex)
            {
                textBox1.Text = ex.Message;
            }
        }

Happy coding !

3 Replies to “Asynchronous tasks on Winform”

  1. How could this be modified to allow the user to automatically cancel the previous async call and start a new async call (with new params) by clicking the button again?

  2. Hi Mark,

    This could be achieved with the following :
    – create a static dictionary containing all current calls
    – AsyncCall is a class containing :
    * the caller (a Control eg: the button)
    * the thread that executes the code

    In the Async() method, add a new instance of call at the begining and remove it at the end.

    If there is already a call with the same caller, kill its thread, remove it from the collection and do a normal call.

    Was it clear enough ?

Leave a Reply

Your email address will not be published. Required fields are marked *