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 !

Make InvokeRequired/Invoke easy

The problem

If you’re working on WinForms, you must know that you cannot call controls methods within a thread that is not the one that created those controls. To solve this problem, Microsoft recommend us to use the following code :

namespace TestWinForm
{
    public partial class MainForm : BaseForm
    {
        public MainForm()
        {
            InitializeComponent();
        }
        // a delegate that has been created specially for this method
        private delegate void DisplayDelegate(string text);

        // a method that may be called from a worker thread
        public virtual void Display(string text)
        {
            if (InvokeRequired)
            {
                Invoke(new DisplayDelegate(Display));
            }
            else
            {
                //the actual job is here
                textBox1.AppendText(text);
            }
        }
    }
}

This code is ugly because :

  • you need to create a delegate for each single public method you can call from outside
  • you need to put an “if/else” block in each method wich increase the complexity of your code

The trick

Here is a trick that could significantly reduce the amount of code needed to do the same job within a large project.

Within you base class

You probably have a common base class for all your UI components. Add this method :

namespace TestWinForm
{
    public class BaseForm : Form
    {
        // 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 ThreadSafe() method :

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

        // public method that may be called from outside and within any
        // worker thread ...
        public virtual void Display(string text)
        {
            ThreadSafe(delegate
            {
                //do any UI related code here
                //note that because this is an anonym method, 
                //you can use the local parameter
                textBox1.AppendText(text);
            });
        }

Happy coding !