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 !