Search code examples
.netwinformsmultithreadingworker

WinForm - implement worker thread just like BackgroundWorker but get the error


    // sample 1 use BackgroundWorker, it works fine
    private BackgroundWorker workerPorcee = new BackgroundWorker();
    private void button1_Click(object sender, EventArgs e)
    {
        workerPorcee.DoWork += new DoWorkEventHandler(workerPorcee_DoWork);
        workerPorcee.ProgressChanged += new ProgressChangedEventHandler(workerPorcee_ProgressChanged);
        workerPorcee.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerPorcee_RunWorkerCompleted);
        workerPorcee.WorkerReportsProgress = true;
        workerPorcee.RunWorkerAsync();
    }
    void workerPorcee_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }
    void workerPorcee_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            workerPorcee.ReportProgress(i);
            Thread.Sleep(100);
        }
    }

    void workerPorcee_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.progressBar1.Value = 100;
    }

I just implement my worker thread, but it will get the exception: Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on.

    // my worker thread
     private void button3_Click(object sender, EventArgs e)
    {
        Worker wk = new Worker();
        Thread thr = new Thread(new ThreadStart(wk.ProcessEvent));
        wk.ProgressChanged += new delCallback(wk_ProgressChanged);
        thr.Start();
    }        

    void wk_ProgressChanged(int percent)
    {
        this.progressBar1.Value = percent;
    //exception:Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on.       

     }

     public delegate void delCallback(int percent);

public class Worker
{
    public event delCallback ProgressChanged;

    public void ProcessEvent()
    {
        for (int i = 0; i < 100; i++)
        {
            if (ProgressChanged != null)
            {
                this.ProgressChanged(i);
            }
            Thread.Sleep(100);
        }
    }
}

WinForm, I just want to use my worker thread to set the progress bar value, but it fail. Any one help to point what is the issue? How to implement the functionality just like BackgroundWorker ?


Solution

  • You need to ensure that all interaction with winForms controls is performed on the main thread that created the control - that is what the exception is telling you. To do this you need to use the Control.Invoke method which will make sure the call to your UI-altering method is performed on the correct thread.

    So, in your code example you need to change it to something like:

    void wk_ProgressChanged(int percent)
    {
        if(this.progressBar1.InvokeRequired)
        {
            this.progressBar1.Invoke(wk_ProgressChanged, new Object[] {percent});
        }
        else
        {
            this.progressBar1.Value = percent;
        }       
     }
    

    The first part checks to see if the method call is being performed on the correct Thread for the progressBar1 control. If we are on a different thread, Invoke is used to call the same method but on the correct thread. Invoke will then call the wk_ProgressChanged method, but this time InvokeRequired will be false (as we are now running on the correct thread to perform the UI update), and the progressBar1 value is set.