从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
还有另一个通用控件扩展。。
首先为Control类型的对象添加扩展方法
public static void InvokeIfRequired<T>(this T c, Action<T> action) where T : Control
{
if (c.InvokeRequired)
{
c.Invoke(new Action(() => action(c)));
}
else
{
action(c);
}
}
并从另一个线程调用类似的方法来访问UI线程中名为object1的控件:
object1.InvokeIfRequired(c => { c.Visible = true; });
object1.InvokeIfRequired(c => { c.Text = "ABC"; });
..或类似的
object1.InvokeIfRequired(c =>
{
c.Text = "ABC";
c.Visible = true;
}
);
其他回答
关于这个主题的另一个示例:我创建了一个抽象类UiSynchronizeModel,它包含一个公共方法实现:
public abstract class UiSynchronizeModel
{
private readonly TaskScheduler uiSyncContext;
private readonly SynchronizationContext winformsOrDefaultContext;
protected UiSynchronizeModel()
{
this.winformsOrDefaultContext = SynchronizationContext.Current ?? new SynchronizationContext();
this.uiSyncContext = TaskScheduler.FromCurrentSynchronizationContext();
}
protected void RunOnGuiThread(Action action)
{
this.winformsOrDefaultContext.Post(o => action(), null);
}
protected void CompleteTask(Task task, TaskContinuationOptions options, Action<Task> action)
{
task.ContinueWith(delegate
{
action(task);
task.Dispose();
}, CancellationToken.None, options, this.uiSyncContext);
}
}
模型或控制器类应从此抽象类派生。您可以使用任何模式(任务或手动管理的后台线程)并使用以下方法:
public void MethodThatCalledFromBackroundThread()
{
this.RunOnGuiThread(() => {
// Do something over UI controls
});
}
任务示例:
var task = Task.Factory.StartNew(delegate
{
// Background code
this.RunOnGuiThread(() => {
// Do something over UI controls
});
});
this.CompleteTask(task, TaskContinuationOptions.OnlyOnRanToCompletion, delegate
{
// Code that can safely use UI controls
});
最简单的方法是调用如下:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
try
{
///
}
catch (Exception)
{
//
}
}
));
当您在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;
}
}
这适用于任务(而不是线程),这是目前编写并发代码的首选方式。
这一解决方案类似于上面使用.NET Framework 3.0的解决方案,但它解决了编译时安全支持的问题。
public static class ControlExtension
{
delegate void SetPropertyValueHandler<TResult>(Control souce, Expression<Func<Control, TResult>> selector, TResult value);
public static void SetPropertyValue<TResult>(this Control source, Expression<Func<Control, TResult>> selector, TResult value)
{
if (source.InvokeRequired)
{
var del = new SetPropertyValueHandler<TResult>(SetPropertyValue);
source.Invoke(del, new object[]{ source, selector, value});
}
else
{
var propInfo = ((MemberExpression)selector.Body).Member as PropertyInfo;
propInfo.SetValue(source, value, null);
}
}
}
要使用:
this.lblTimeDisplay.SetPropertyValue(a => a.Text, "some string");
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, false);
如果用户传递错误的数据类型,编译器将失败。
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, "sometext");
WPF应用程序中最简单的方法是:
this.Dispatcher.Invoke((Action)(() =>
{
// This refers to a form in a WPF application
val1 = textBox.Text; // Access the UI
}));