我想我需要对WPF Dispatcher.Invoke 和 Dispatcher.BeginInvoke 的使用做一些澄清。
假设我有一些长时间运行的“工作”代码,就像在简单的WPF应用程序中按下按钮一样:
longWorkTextBox.Text = "Ready For Work!";
Action workAction = delegate
{
Console.WriteLine("Starting Work Action");
int i = int.MaxValue;
while (i > 0)
i--;
Console.WriteLine("Ending Work Action");
longWorkTextBox.Text = "Work Complete";
};
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);
此代码在执行 workAction 时锁定了我的用户界面。这是因为Dispatcher调用总是在UI线程上运行,对吗?
假设这样,配置我的调度程序在我的UI的单独线程中执行 workAction 的最佳做法是什么?我知道我可以添加 BackgroundWorker 到我的 workAction 以阻止我的UI锁定:
longWorkTextBox.Text = "Ready For Work!";
Action workAction = delegate
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
Console.WriteLine("Starting Slow Work");
int i = int.MaxValue;
while (i > 0)
i--;
Console.WriteLine("Ending Work Action");
};
worker.RunWorkerCompleted += delegate
{
longWorkTextBox.Text = "Work Complete";
};
worker.RunWorkerAsync();
};
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);
除了使用 BackgroundWorker 之外,还有更优雅的方法吗?我一直听说 BackgroundWorker 很古怪,所以我很想知道一些替代方案。
答案 0 :(得分:25)
老实说,我认为BackgroundWorker
是最优雅的解决方案。我想不出更简单的方法。
答案 1 :(得分:4)
查理的回答是你正在寻找的,真的。
但是,如果可能的话,您可能会考虑是否可以将您的工作包括在内,以便各个工作单元都很小,并且不会对UI造成太大影响。这将允许您直接使用Dispatcher。在WPF线程页面上有一个很好的例子:https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx
答案 2 :(得分:4)
我也不喜欢BackgroundWorker。 一个简单的替代方案可能是:
using System;
using System.Threading;
using System.Windows;
namespace Sample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
longWorkTextBox.Text = "Ready For Work!";
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
new Thread(Work).Start();
}
void Work()
{
longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; }));
Console.WriteLine("Starting Work Action");
int i = int.MaxValue;
while (i > 0)
i--;
Console.WriteLine("Ending Work Action");
longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; }));
}
}
}
容易,不是吗?
答案 3 :(得分:2)
正如其名称所示,它将在后台执行,因此您无需使用Dispatcher对其进行实例化。此外,如果您希望此代码运行到WP7中,则BeginInvoke不会获取背景参数。
我的建议是将BackgroundWorker创建为:
BackgroundWorker worker = new BackgroundWorker;
然后为事件创建处理程序:
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork +=new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged);
最后你打电话:
bkwkPlayingLoop.RunWorkerAsync();
从DoWork内部使用Dispatcher是一个很大的诱惑,而是调用worker.ReportProgress()并从那里处理UI。否则你将面临与解雇终止事件的一些不一致。
答案 4 :(得分:0)
任务比后台工作人员更容易使用,做更多事情,问题更少,并且几乎创建了这些任务,因此后台工作人员不再需要使用......