Accessing controls from another thread in C#



When we try to access a control from another thread, you may see an exception, saying 'InvalidOperationException'. Like below



This is raised because a control was accessed without invoking.
One method is to check if that particular object required to be invoked.
If it needs to be invoked, Invoke it else just access it.
A simple way to do this is described in this post.

First Add these references in your code


using System.Threading;


Declare two delegates as given below


private delegate void DSetText(string txt);

private delegate void DSetEnable(Control control, bool enable);



Define the function to change the text of a control as below


private void SetText(string text)
{

    //check if the label needs to be invoked
    if (lblText.InvokeRequired)
    {
       //create an object of the delegate. The argument is the method (SetText) itself
       DSetText d = new DSetText(SetText);
       //now lets invoke it. pass the arguments of the method as an object array
       this.Invoke(d, new object[] { text });
     }
     else
     {
        //no invoke required so lets set the text
        lblText.Text = text;
        //sometimes the label needs to be refreshed to view the changed text
        lblText.Refresh();
        
     }
}


Define the function to enable/disable a control as below



private void SetEnable(Control control,bool enable)
{
   //check if the control needs to be invoked
   if (control.InvokeRequired)
   {
      //create an object of the delegate the argument is the method (SetText) itself
      DSetEnable d = new DSetEnable(SetEnable);
      //now lets invoke it. pass the arguments of the method as an object array
      this.Invoke(d, new object[] { control,enable });
   }
   else
   {
      //no invoke required so lets enable/disable the control
      control.Enabled = enable;
   }
}


Declare an object of the Thread. This is the thread where a time consuming work executes 



Thread myThread = null;


Declare a variable to stop execution of the thread. When this value is True then thread stops  execution.



bool StopRequested = false;


Define a function to execute in the thread (myThread)



public void StartRunning()
{
   int count = 0;
   //disable the start button
   SetEnable(btnStart, false);
   //enable stop button
   SetEnable(btnStop, true);
           
  //execute until StopRequested is true. StopRequested is set when btnStop is clicked.
   while(!StopRequested)
   {
     //set the count as label's text
     SetText(count.ToString());
     //lets wait for some time
     Thread.Sleep(100);
     count++;
   }

   //enable the start button
   SetEnable(btnStart, true);
   //disable stop button
   SetEnable(btnStop, false);

}


Now on the Click event of the button( Button to start the thread) add the below code



StopRequested = false;
myThread = new Thread(new ThreadStart(StartRunning));
myThread.Start();           


Now on the Click event of the other button( Button to stop the thread execution)



StopRequested = true;


The overall code is given below


 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Windows.Forms;
 using System.Threading;

 namespace ThreadSafeApp
 {
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //Delegate to set the text
        private delegate void DSetText(string txt);
        //delegate to enable/disable a control
        private delegate void DSetEnable(Control control, bool enable);


        //Method to set the text of label
        private void SetText(string text)
        {
            //check if the label needs to be invoked
            if (lblText.InvokeRequired)
            {
        
            //create an object of the delegate. The argument is the method (SetText) itself
                DSetText d = new DSetText(SetText);
                //now lets invoke it. pass the arguments of the method as an object array
                this.Invoke(d, new object[] { text });
            }
            else
            {
                //no invoke required so lets set the text
                lblText.Text = text;
                //sometimes the label needs to be refreshed to view the changed text
                lblText.Refresh();
            }
        }

        private void SetEnable(Control control,bool enable)
        {
            //check if the control needs to be invoked
            if (control.InvokeRequired)
            {

            //create an object of the delegate. The argument is the method (SetText) itself
                DSetEnable d = new DSetEnable(SetEnable);
                //now lets invoke it. pass the arguments of the method as an object array
                this.Invoke(d, new object[] { control,enable });
            }
            else
            {
                //no invoke required so lets enable/disable the control
                control.Enabled = enable;
            }
        }

        //thread
        Thread myThread = null;

       
      
        bool StopRequested = false;
       
        public void StartRunning()
        {
            int count = 0;
            //disable the start button
            SetEnable(btnStart, false);
            //enable stop button
            SetEnable(btnStop, true);
           

            //execute until StopRequested is true.
            //StopRequested is set when btnStop is clicked.
            while(!StopRequested)
           {
               //set the count as label's text
               SetText(count.ToString());
               //lets wait for some time
               Thread.Sleep(100);
               count++;
           }

            //enable the start button
            SetEnable(btnStart, true);
            //disable stop button
            SetEnable(btnStop, false);

        }
       
        private void btnStart_Click(object sender, EventArgs e)
        {
            StopRequested = false;
            myThread = new Thread(new ThreadStart(StartRunning));
            myThread.Start();          
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            StopRequested = true;         
        }
    }
 }



Comments