从另一个线程更新标签的最简单方法是什么?

我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。

我怎么能这样做?


当前回答

简单的解决方案是使用Control.Invoke。

void DoSomething()
{
    if (InvokeRequired) {
        Invoke(new MethodInvoker(updateGUI));
    } else {
        // Do Something
        updateGUI();
    }
}

void updateGUI() {
    // update gui here
}

其他回答

您必须确保更新发生在正确的线程上;UI线程。

为了做到这一点,您必须调用事件处理程序,而不是直接调用它。

您可以通过以下方式发起活动:

(代码是在我的脑海中打出来的,所以我没有检查语法等是否正确,但它应该能让你继续。)

if( MyEvent != null )
{
   Delegate[] eventHandlers = MyEvent.GetInvocationList();

   foreach( Delegate d in eventHandlers )
   {
      // Check whether the target of the delegate implements 
      // ISynchronizeInvoke (Winforms controls do), and see
      // if a context-switch is required.
      ISynchronizeInvoke target = d.Target as ISynchronizeInvoke;

      if( target != null && target.InvokeRequired )
      {
         target.Invoke (d, ... );
      }
      else
      {
          d.DynamicInvoke ( ... );
      }
   }
}

请注意,上面的代码在WPF项目上不起作用,因为WPF控件不实现ISynchronizeInvoke接口。

为了确保上面的代码适用于Windows窗体和WPF以及所有其他平台,您可以查看AsyncOperation、AsyncOperationManager和SynchronizationContext类。

为了以这种方式轻松地引发事件,我创建了一个扩展方法,它允许我通过调用以下命令来简化引发事件:

MyEvent.Raise(this, EventArgs.Empty);

当然,您也可以使用BackGroundWorker类,它将为您抽象这个问题。

出于许多目的,它非常简单:

public delegate void serviceGUIDelegate();
private void updateGUI()
{
  this.Invoke(new serviceGUIDelegate(serviceGUI));
}

“serviceGUI()”是表单(this)中的GUI级方法,可以根据需要更改任意数量的控件。从另一个线程调用“updateGUI()”。可以添加参数来传递值,或者(可能更快)根据需要使用带有锁的类范围变量,如果访问这些变量的线程之间可能发生冲突,从而导致不稳定。如果非GUI线程是时间关键的(请记住BrianGideon的警告),请使用BeginInvoke而不是Invoke。

即使操作很耗时(在我的示例中为thread.sleep)-此代码不会锁定您的UI:

 private void button1_Click(object sender, EventArgs e)
 {

      Thread t = new Thread(new ThreadStart(ThreadJob));
      t.IsBackground = true;
      t.Start();         
 }

 private void ThreadJob()
 {
     string newValue= "Hi";
     Thread.Sleep(2000); 

     this.Invoke((MethodInvoker)delegate
     {
         label1.Text = newValue; 
     });
 }

必须使用invoke和delegate

private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });

例如,访问当前线程以外的控件:

Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{
    lblThreshold.Text = Speed_Threshold.ToString();
}));

lblThreshold是一个标签,Speed_Threshold则是一个全局变量。