如何在不同的线程中引用控件?

时间:2012-08-27 21:28:12

标签: c# multithreading backgroundworker

//button is clicked
//worker starts

private void processWorker_DoWork(object sender, DoWorkEventArgs e)
{
     string code = DoLongWorkAndReturnCode();

     if (code != 0)
     {
         MessageBox.Show("Error!");
         EnableAllButtons(); // this is defined in the other thread and it's where i run into the error.
     }
     else
     {
          string code = DoAnotherLongProcessAndReturnCode(); 
          if (code != 0)
          {
               MessageBox.Show("Error 2!");
               EnableAllButtons(); // again, this is defined in the other thread
          }
     }
}

我遇到了交叉线程错误,因为“EnableAllButtons()”是在不同的线程中定义的。

如何从一个不同的线程启用一个线程中的所有按钮?

3 个答案:

答案 0 :(得分:2)

根据您的姓名,我假设您使用的是BackgroundWorker。您必须在ProgressChangedRunWorkerCompletedControl.InvokeControl.BeginInvoke中更新控件(您无需为Control的BeginInvoke触发EndInvoke)。

我的建议是在后台工作程序中抛出异常(如果你没有使用除检查0之外的代码,如果不使用Result方法。)并检查传入的参数的Error属性。 RunWorkerCompleted事件。从那里你可以启动另一个后台工作人员或停止。您将在主线程上运行,因此您可以在不调用的情况下更改按钮。


示例:通过BeginInvoke启用按钮

使用此示例,您可以使用您的代码,只需修改EnableAllButtons

即可
private void EnableAllButtons() 
{ 
    //See if the main form needs invoke required, assuming the buttons are on the same thread as the form
    if(this.InvokeRequired) 
    {
        //Calls EnableAllButtons a seccond time but this time on the main thread.
        //This does not block, it is "fire and forget"
        this.BeginInvoke(new Action(EnableAllButtons));
    }
    else
    {
        btnProcessImages.Enabled = true; 
        btnBrowse.Enabled = true; 
        btnUpload.Enabled = true; 
        btnExit.Enabled = true; 
        ControlBox = true;
    }
}

示例:通过结果返回数据

private void processWorker1_DoWork(object sender, DoWorkEventArgs e)
{
     string code = DoLongWorkAndReturnCode();
     e.Result = code;
}
private void processWorkers_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (code != 0)
    {
        MessageBox.Show("Error!");
        EnableAllButtons(); // this is defined in the other thread and it's where i run into the error.
    }
    else
    {
          doAnotherLongProcessAndReturnCodesBackgroundWorker.RunWorkerAsync(); 
    }
}

示例:通过异常+重用事件处理程序返回数据。

private void processWorker1_DoWork(object sender, DoWorkEventArgs e)
{
     string code = DoLongWorkAndReturnCode();
     if (code != 0)
     {
         thow new MyCustomExecption(code);
     }
}

private void processWorker2_DoWork(object sender, DoWorkEventArgs e)
{
     string code = DoAnotherLongProcessAndReturnCode(); 
     if (code != 0)
     {
         thow new MyCustomExecption(code);
     }
}

private void processWorkers_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if(e.Error != null)
    {
        string message;

        //See if it was our error
        if(e.Error.GetType() == typeOf(MyCustomExecption))
        {

            //Choose which error message to send
            if(sender.Equals(processWorker2))
                message = "Error2!";
            else
                message = "Error!";
        }
        else
        {
            //Handle other types of thrown exceptions other than the ones we sent
            message = e.Error.ToString();
        }


        //no matter what, show a dialog box and enable all buttons.
        MessageBox.Show(message);
        EnableAllButtons();
    }
    else
    {
        //Worker completed successfully. 
        //If this was called from processWorker1 call processWorker2

        //Here is the code that was returned from the function
        if(sender.Equals(processWorker1))
            processWorker2.RunWorkerAsync();
    }

}

答案 1 :(得分:0)

为了访问另一个线程中的控件,您需要使用Control.Invoke,如下所述: Control.Invoke Method (Delegate)

答案 2 :(得分:0)

如果您必须直接从另一个线程访问UI控件,请查看这个最有用的扩展方法

https://stackoverflow.com/a/3588137/141172

BackgroundWorker提供an event for progress updates。如果您对UI的更改具有此类性质,请考虑使用该事件。