您可以BackroundWorker
坚持,如果你选择。虽然这是非常古老的学校,但它并没有真正的黑客。正如有人说,如果你不能在你的工具箱中找到它,你可以随时申报,并直接从您的代码初始化它(不要忘了using System.ComponentModel;
指令)。
Stephen Cleary在BackgroundWorker
和Task
上有一系列优秀的博客文章,突出了每种方法的差异和局限性。这绝对值得一读,如果你在围栏或只是好奇。
http://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html
如果你决定往下走Task
+ async/await
路线,有一对夫妇的进展报告,你应该记住具体相关的东西。
一般来说,你应该瞄准有你await Task.Run
封装最小有意义的工作可能量。您async
方法的其余部分,然后将执行的调度SynchronizationContext
(假设它开始调度线程),将能够直接更新UI,像这样:
List<object> items = GetItemsToProcess();
int doneSoFar = 0;
foreach (var item in items)
{
await Task.Run(() => SomeCpuIntensiveWorkAsync(item));
doneSoFar++;
int progressPercentage = (int)((double)doneSoFar/items.Count * 100);
// Update the UI.
this.ProgressBar.Value = progressPercentage;
}
这是最简单的方法在async
世界中实施进度报告。
唯一的一次,我能想象报告从委托体内传递给Task.Run
的进展是,当你处理非常大量物品,以及每个项目的处理需要很短的时间(我们每秒钟讲10000个项目作为粗略指南)。在这种情况下,创建大量非常细粒度的和将会引入大量开销的场景。如果这是您的情况,您可以回到.NET 4中引入的进度报告机制:Progress<T>
/IProgress<T>
。这与BackgroundWorker
报告的进展方式非常相似(因为它依赖于事件),它在决定何时回发到调度程序上下文方面提供了更多的灵活性。
public async Task DoWorkAsync()
{
// Let's assume we're on the UI thread now.
// Dummy up some items to process.
List<object> items = GetItemsToProcess();
// Wire up progress reporting.
// Creating a new instance of Progress
// will capture the SynchronizationContext
// any any calls to IProgress.Report
// will be posted to that context.
Progress<int> progress = new Progress<int>();
progress.ProgressChanged += (sender, progressPercentage) =>
{
// This callback will run on the thread which
// created the Progress<int> instance.
// You can update your UI here.
this.ProgressBar.Value = progressPercentage;
};
await Task.Run(() => this.LongRunningCpuBoundOperation(items, progress));
}
private void LongRunningCpuBoundOperation(List<object> items, IProgress<int> progress)
{
int doneSoFar = 0;
int lastReportedProgress = -1;
foreach (var item in items)
{
// Process item.
Thread.Sleep(1);
// Calculate and report progress.
doneSoFar++;
var progressPercentage = (int)((double)doneSoFar/items.Count * 100);
// Only post back to the dispatcher SynchronizationContext
// if the progress percentage actually changed.
if (progressPercentage != lastReportedProgress)
{
// Note that progress is IProgress<int>,
// not Progress<int>. This is important
// because Progress<int> implements
// IProgress<int>.Report explicitly.
progress.Report(progressPercentage);
lastReportedProgress = progressPercentage;
}
}
}
“BackgroundWorker”类是在WPF中的后台线程上更新UI的主要方法之一。你看过哪些文章表明这很黑客? –
谢谢@AndrewStephens--我认为这很荒谬的原因是因为1)默认情况下它不在WPF应用程序的工具箱中,2)您必须手动添加worker.SupportsProgress = true; (由于缺少此控件的属性窗格,3)我可以看到其他人已经表示BackgroundWOrker无法使用委托(在WinForms中不需要)从WorkerCompleted或ProgressChanged事件内部更新UI - 这导致我相信也许BGWorker不完全支持或不打算在WPF应用程序中使用。 –
这是一个不是UI控件的类,所以它不在工具箱中。您通常会在您的代码隐藏内部创建它的实例,或者如果使用MVVM,您将创建它的视图模型。 ProgressChanged事件在UI线程中引发,因此不需要委托。我确信WorkerCompleted也是如此,但是如果不是在“Dispatcher.Invoke()'调用中包装UI更新代码。再次,您会发现这是WPF中的常用技术,尤其是在从其他线程更新UI时。 –