1
//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()”是在不同的线程中定义的。

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

4

3 回答 3

2

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

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


示例:通过 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;
    }
}

示例:通过 Result 返回数据

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();
    }

}
于 2012-08-27T21:36:57.993 回答
0

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

于 2012-08-27T21:31:40.847 回答
0

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

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

BackgroundWorker为进度更新提供了一个事件。如果您对 UI 的更改属于这种性质,请考虑使用该事件。

于 2012-08-27T21:33:26.000 回答