从另一个线程更新一个 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。 虽然不是完美的,但是仍然比我们想象的要好