从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
在这个问题上,大多数其他答案对我来说都有点复杂(我是C#新手),所以我写了我的答案:
我有一个WPF应用程序,并定义了如下工作人员:
问题:
BackgroundWorker workerAllocator;
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1) {
// This is my DoWork function.
// It is given as an anonymous function, instead of a separate DoWork function
// I need to update a message to textbox (txtLog) from this thread function
// Want to write below line, to update UI
txt.Text = "my message"
// But it fails with:
// 'System.InvalidOperationException':
// "The calling thread cannot access this object because a different thread owns it"
}
解决方案:
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1)
{
// The below single line works
txtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}
我还没有弄清楚上面这句话的意思,但它奏效了。
对于WinForms:
解决方案:
txtLog.Invoke((MethodInvoker)delegate
{
txtLog.Text = "my message";
});
其他回答
简单的解决方案是使用Control.Invoke。
void DoSomething()
{
if (InvokeRequired) {
Invoke(new MethodInvoker(updateGUI));
} else {
// Do Something
updateGUI();
}
}
void updateGUI() {
// update gui here
}
出于许多目的,它非常简单:
public delegate void serviceGUIDelegate();
private void updateGUI()
{
this.Invoke(new serviceGUIDelegate(serviceGUI));
}
“serviceGUI()”是表单(this)中的GUI级方法,可以根据需要更改任意数量的控件。从另一个线程调用“updateGUI()”。可以添加参数来传递值,或者(可能更快)根据需要使用带有锁的类范围变量,如果访问这些变量的线程之间可能发生冲突,从而导致不稳定。如果非GUI线程是时间关键的(请记住BrianGideon的警告),请使用BeginInvoke而不是Invoke。
在我的案例(WPF)中,解决方案简单如下:
private void updateUI()
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(updateUI);
return;
}
// Update any number of controls here
}
只需使用以下内容:
this.Invoke((MethodInvoker)delegate
{
progressBar1.Value = e.ProgressPercentage; // runs on UI thread
});
这是Ian Kemp解决方案的C#3.0变体:
public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control
{
var memberExpression = property.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("The 'property' expression must specify a property on the control.");
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
throw new ArgumentException("The 'property' expression must specify a property on the control.");
if (control.InvokeRequired)
control.Invoke(
(Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread,
new object[] { control, property, value }
);
else
propertyInfo.SetValue(control, value, null);
}
你这样称呼它:
myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
它将空检查添加到“as MemberExpression”的结果中。它提高了静态类型的安全性。
否则,原始版本是一个非常好的解决方案。