如何从另一个线程更新 GUI?

从另一个线程更新一个 Label 最简单的方法是什么?
在 thread1上有一个 Form,从这个表单开始启动另一个线程(thread2)。 虽然 thread2正在处理一些文件,但我希望用 thread2的工作的当前状态更新 Form 上的 Label。
怎么做?

马克 · 格拉弗尔最简变易方法Marc Gravell's simplest solution
control.Invoke((MethodInvoker) (() => control.Text = "new text"));
或者使用 Action delegate 代替:
control.Invoke(new Action(() => control.Text = "new text"));

最简单的方法是将一个匿名方法传到Label.Invoke:

// Running on the worker threadstring newText = "abc";
form.Label.Invoke((MethodInvoker)delegate {
    // Running on the UI thread
    form.Label.Text = newText;});// Back on the worker thread

请注意,Invoke 会阻止执行,直到它完成为止——这是同步代码。 这个问题并不是关于异步代码,CSDN上有很多关于编写异步代码的内容可以用。

对于Net 2.0,这里有一段我写的代码,它可以完全满足你的需要,并且适用于Control:

private delegate void SetControlPropertyThreadSafeDelegate(
    Control control, 
    string propertyName, 
    object propertyValue);
public static void SetControlPropertyThreadSafe(
    Control control, 
    string propertyName, 
    object propertyValue){
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
        propertyName, 
        BindingFlags.SetProperty, 
        null, 
        control, 
        new object[] { propertyValue });
  }}

可以这样:
// thread-safe equivalent of// myLabel.Text = status;SetControlPropertyThreadSafe(myLabel, "Text", status);
如果你正在使用 .Net 3.0及以上版本,你可以重写上面的方法作为 Control 类的扩展方法,这样可以简化调用:
myLabel.SetPropertyThreadSafe("Text", status);
For you should use this code:
对 .NET 3.0,使用下面代码:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
    Control @this, 
    Expression<Func<TResult>> property, 
    TResult value);
public static void SetPropertyThreadSafe<TResult>(
    this Control @this, 
    Expression<Func<TResult>> property, 
    TResult value){
  var propertyInfo = (property.Body as MemberExpression).Member 
      as PropertyInfo;

  if (propertyInfo == null ||
      !@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
          propertyInfo.Name, 
          propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
      @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }}

用 LINQ 和 lambda 表达式来支持更加简洁、简单和安全的语法:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
现在不仅要在编译时检查属性名,而且还要检查属性的类型,因此不可能将字符串值分配给布尔属性,从而导致运行异常。
可惜还是有人会做错,比如传另一个 Control的属性和值,所以下面的代码可以编译:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
因此,我添加了运行检查,以确保传入的属性实际上属于正在调用该方法的 Control。 虽然不是完美的,但是仍然比我们想象的要好