in Development

Accessing UI controls with Multithreading applications

If you work with multithreading applications, probably you will need to access UI controls created in the main thread from a different thread. As you know, one of the rules of Windows programming, is that the changes done over UI controls must be done from the thread that created the control. Otherwise with .net 2.0 an exception will be thrown at execution time, but in previous versions of .net it is worse since you can obtain unexpected results.

Let’s see a simple sample that would create troubles:

   1: private void bttStart_Click(object sender, System.EventArgs e)
   2: {
   3:     System.Threading.Thread thread = new System.Threading.Thread(
   4:         new System.Threading.ThreadStart(DoWork));
   5:     thread.Name = "Worker Thread";
   6:     thread.IsBackground = true;
   7:     thread.Start();
   8: }
   9:  
  10: private void DoWork()
  11: {
  12:     int iteration = 0;
  13:     while (true)
  14:     {
  15:         iteration++;
  16:         textBox1.Text = iteration.ToString(
  17:             System.Globalization.CultureInfo.InvariantCulture);
  18:         System.Threading.Thread.Sleep(3000);
  19:     }
  20: }

Therefore this is avoidable just adding a few lines more. All the UI controls have a property called InvokeRequired, which indicates if the caller comes from a different thread than the one that created the control. In this case we only need to invoke the method again in the right thread. Let’s see how to fix it:

   1: public delegate void TextUpdate(string text);
   2:  
   3: private void SetText(string text)
   4: {
   5:     if (textBox1.InvokeRequired)
   6:     {
   7:         textBox1.Invoke(new TextUpdate(SetText), text);
   8:     }
   9:     else
  10:     {
  11:         textBox1.Text = text;
  12:     }
  13: }

As you can see the changes are very simple. We have created a method called SetText, which checks if the method that is going to modify the Text property is in a different thread, if that’s the case it creates an instance of the TextUpdate delegate. The delegate just points to the method SetText. Now when you call the Invoke method of the TextBox, the only thing that it will do is to call again the method SetText but this time from the thread which created the control.