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

14 浏览
0 Comments

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

如何从另一个线程简单地更新Label

  • 我有一个在线程1上运行的Form,并且从那里我启动了另一个线程(线程2)。
  • 线程2处理一些文件时,我想通过Form上的Label来更新线程2的工作状态。

我该如何做?

admin 更改状态以发布 2023年5月21日
0
0 Comments

对于.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);

更新05/10/2010:

对于.NET 3.0,你应该使用这段代码:

private delegate void SetPropertyThreadSafeDelegate(
    Control @this, 
    Expression> property, 
    TResult value);
public static void SetPropertyThreadSafe(
    this Control @this, 
    Expression> 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 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }
}

它使用LINQ和lambda表达式来允许更清晰、更简单、更安全的语法:

// status has to be of type string or this will fail to compile
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status);

现在不仅属性名称在编译时进行了检查,属性的类型也进行了检查,因此不可能(例如)将字符串值赋给布尔属性,从而导致运行时异常。

不幸的是,这并不会阻止任何人做一些愚蠢的事情,比如传递另一个 Control 的属性和值,因此下面的代码将愉快地编译:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

因此,我添加了运行时检查,以确保传入的属性实际上属于调用该方法的 Control。虽然不完美,但仍然比.NET 2.0版本好得多。

如果有人对如何改进这段代码以实现编译时安全性有任何进一步的建议,请留言!

0
0 Comments

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

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

请注意,Invoke会阻塞执行直到它执行完成-这是同步代码。虽然问题没有要求关于异步代码,但是如果你想学习关于异步代码的话,在Stack Overflow上有很多相关的内容

0