从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
WPF应用程序中最简单的方法是:
this.Dispatcher.Invoke((Action)(() =>
{
// This refers to a form in a WPF application
val1 = textBox.Text; // Access the UI
}));
其他回答
当您在UI线程中时,可以向它请求其同步上下文任务调度器。它将为您提供一个TaskScheduler,用于调度UI线程上的所有内容。
然后,您可以将任务链接起来,这样当结果准备就绪时,另一个任务(在UI线程上安排)会选择它并将其分配给标签。
public partial class MyForm : Form
{
private readonly TaskScheduler _uiTaskScheduler;
public MyForm()
{
InitializeComponent();
_uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void buttonRunAsyncOperation_Click(object sender, EventArgs e)
{
RunAsyncOperation();
}
private void RunAsyncOperation()
{
var task = new Task<string>(LengthyComputation);
task.ContinueWith(antecedent =>
UpdateResultLabel(antecedent.Result), _uiTaskScheduler);
task.Start();
}
private string LengthyComputation()
{
Thread.Sleep(3000);
return "47";
}
private void UpdateResultLabel(string text)
{
labelResult.Text = text;
}
}
这适用于任务(而不是线程),这是目前编写并发代码的首选方式。
出于许多目的,它非常简单:
public delegate void serviceGUIDelegate();
private void updateGUI()
{
this.Invoke(new serviceGUIDelegate(serviceGUI));
}
“serviceGUI()”是表单(this)中的GUI级方法,可以根据需要更改任意数量的控件。从另一个线程调用“updateGUI()”。可以添加参数来传递值,或者(可能更快)根据需要使用带有锁的类范围变量,如果访问这些变量的线程之间可能发生冲突,从而导致不稳定。如果非GUI线程是时间关键的(请记住BrianGideon的警告),请使用BeginInvoke而不是Invoke。
只使用ui的同步上下文
using System.Threading;
// ...
public partial class MyForm : Form
{
private readonly SynchronizationContext uiContext;
public MyForm()
{
InitializeComponent();
uiContext = SynchronizationContext.Current; // get ui thread context
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() =>
{// set ui thread context to new thread context
// for operations with ui elements to be performed in proper thread
SynchronizationContext
.SetSynchronizationContext(uiContext);
label1.Text = "some text";
});
t.Start();
}
}
这是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”的结果中。它提高了静态类型的安全性。
否则,原始版本是一个非常好的解决方案。
简单的解决方案是使用Control.Invoke。
void DoSomething()
{
if (InvokeRequired) {
Invoke(new MethodInvoker(updateGUI));
} else {
// Do Something
updateGUI();
}
}
void updateGUI() {
// update gui here
}