Asynchronous tasks on Winform

Posted on : 11-05-2009 | By : manitra | In : C#, Developpement, WinForm

2

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 :

  1. namespace TestWinForm
  2. {
  3.     public class BaseForm : Form
  4.     {
  5.         // Execute some code in async mode.
  6.         // When it's done, it calls the nextStep delegate, eventually with
  7.         // an exception catched during the main action.
  8.         protected virtual void Async(Action action, Action nextStep)
  9.         {
  10.             new Thread(delegate()
  11.             {
  12.                 Exception exception = null;
  13.                 try
  14.                 {
  15.                     action();
  16.                 }
  17.                 catch (Exception ex)
  18.                 {
  19.                     exception = ex;
  20.                 }
  21.                 ThreadSafe(() => { nextStep(exception ); });
  22.             }).Start();
  23.         }
  24.  
  25.         // This allows a sub class to easily run a method within
  26.         // an UI thread without the need of creating multiple
  27.         // delegate signatures for each method signatures
  28.         protected virtual void ThreadSafe(MethodInvoker method)
  29.         {
  30.             if (InvokeRequired)
  31.                 Invoke(method);
  32.             else
  33.                 method();
  34.         }
  35.     }
  36. }

Within your UI classes

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

namespace TestWinForm
  1. {
  2.     public partial class MainForm : BaseForm
  3.     {
  4.         public MainForm()
  5.         {
  6.             InitializeComponent();
  7.         }
  8.  
  9.         // Here is the async trick :
  10.         // – UI will NOT freeze,
  11.         // – you can add beautifull animated gifs
  12.         private void button1_Click(object sender, EventArgs e)
  13.         {
  14.             DateTime? result = null;
  15.             Async(
  16.                 () =>
  17.                 {
  18.                     result = GetComplexDate();
  19.                 },
  20.                 (ex) =>
  21.                 {
  22.                     if (ex == null)
  23.                         textBox1.Text = result.Value.ToShortDateString();
  24.                     else
  25.                         textBox1.Text = ex.Message;
  26.                 }
  27.             );
  28.         }
  29.  
  30.         // This is the slow, data-intensive task :p
  31.         private DateTime? GetComplexDate()
  32.         {
  33.             Thread.Sleep(3000);
  34.             return DateTime.Now;
  35.         }
  36.     }
  37. }

The traditional way

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

// This was traditional way :
  1.         // – UI will freeze until during 3 second …
  2.         // – you users will complain
  3.         // avoid this !
  4.         private void button1_Click2(object sender, EventArgs e)
  5.         {
  6.             try
  7.             {
  8.                 textBox1.Text = GetComplexDate().ToShortDateString();
  9.             }
  10.             catch (Exception ex)
  11.             {
  12.                 textBox1.Text = ex.Message;
  13.             }
  14.         }

Happy coding !

Make InvokeRequired/Invoke easy

Posted on : 23-01-2009 | By : manitra | In : C#, Developpement, WinForm

5

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 :

  1. namespace TestWinForm
  2. {
  3.     public partial class MainForm : BaseForm
  4.     {
  5.         public MainForm()
  6.         {
  7.             InitializeComponent();
  8.         }
  9.         // a delegate that has been created specially for this method
  10.         private delegate void DisplayDelegate(string text);
  11.  
  12.         // a method that may be called from a worker thread
  13.         public virtual void Display(string text)
  14.         {
  15.             if (InvokeRequired)
  16.             {
  17.                 Invoke(new DisplayDelegate(Display));
  18.             }
  19.             else
  20.             {
  21.                 //the actual job is here
  22.                 textBox1.AppendText(text);
  23.             }
  24.         }
  25.     }
  26. }

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 :

  1. namespace TestWinForm
  2. {
  3.     public class BaseForm : Form
  4.     {
  5.         // This allows a sub class to easily run a method within
  6.         // an UI thread without the need of creating multiple
  7.         // delegate signatures for each method signatures
  8.         protected virtual void ThreadSafe(MethodInvoker method)
  9.         {
  10.             if (InvokeRequired)
  11.                 Invoke(method);
  12.             else
  13.                 method();
  14.         }
  15.     }
  16. }

Within your UI classes

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

  1. namespace TestWinForm
  2. {
  3.     public partial class MainForm : BaseForm
  4.     {
  5.         public MainForm()
  6.         {
  7.             InitializeComponent();
  8.         }
  9.  
  10.         // public method that may be called from outside and within any
  11.         // worker thread …
  12.         public virtual void Display(string text)
  13.         {
  14.             ThreadSafe(delegate
  15.             {
  16.                 //do any UI related code here
  17.                 //note that because this is an anonym method,
  18.                 //you can use the local parameter
  19.                 textBox1.AppendText(text);
  20.             });
  21.         }

Happy coding !