Donnerstag, 15. Januar 2015

[C#] Accessing Form Controls from another thread / class

While working on a new advertising tool I thought a progressbar indicating how many players stil need to be whispered would be pretty cool:


However one will face problems directly accessing the progressbar from another thread since our thread can collide with the thread which created the progressbar (the UI thread).

To avoid those crashes C# offers a method Invoke which requests another thread to execute code through a delegate (read more).

Speaking in code:

First of all we create a delegate which takes an int as parameter:

 private delegate void updatePbDelegate(int percentage);  

Following that step we create a function which will be called by the other thread:

 public void updatePb(int percentage)  
 {  
      if (this.InvokeRequired)  
      {  
           updatePbDelegate set = new updatePbDelegate(updatePb);  
           this.Invoke(set, new object[] { percentage });  
      }  
      else  
      {  
           pbSender.Value = percentage;  
      }  
 }  

InvokeRequired compares the callers thread with the thread that created the GUI (read more).
If the call comes from another thread a delegate instance pointing to updatePb will be created and passed to Invoke which will then proceed and call updatePb from the UI thread. This time InvokeRequired will return false and the value of the progressbar will be adjusted.

Last but not least we need to pass our Form instance to the thread which will look like this:

 public partial class Form1 : Form  
 {  
      public Form1()  
      {  
           InitializeComponent();  
           Sender.Start(this);  
      }  
      private delegate void updatePbDelegate(int percentage);  
      public void updatePb(int percentage)  
      {  
           if (this.InvokeRequired)  
           {  
                updatePbDelegate set = new updatePbDelegate(updatePb);  
                this.Invoke(set, new object[] { percentage });  
           }  
           else  
           {  
                pbSender.Value = percentage;  
           }  
      }  
 }  
 internal static class Sender  
 {  
      private static Thread thrSender;  
      private static Form1 formAccess;  
      internal static void Start(Form1 parForm)  
      {  
           formAccess = parForm;  
           thrSender = new Thread(Pulse);  
           thrSender.IsBackground = true;  
           thrSender.Start();  
      }  
      private static void Pulse()  
      {  
           formAccess.updatePb(100);  
      }  
 }