我有一个用于大型CSV文件的解析器类。解析方法逐行读取大文件的工作是在backgroundWorker中完成的。使用backgroundWorker.ReportProgress
方法将完整信息百分比传递给UI线程,以便表单上的进度条可以完成其任务。这很好。
但是,我还想提出一个自定义事件,它将从CSV文件第一行获取的字段名列表发回UI(WPF),以便将它们放在下拉列表中。如果解析器遇到格式错误的线路或其他路障,我还想通过事件通知用户。
我的解析器进程可以在后台执行只是引发一个事件吗?或者必须将SynchronizationContext.Current从主UI线程传递到我的解析器类,然后使用Post方法?
答案 0 :(得分:8)
如果绝对必须在DoWork
处理程序中安排UI线程的工作,最简单的方法是使用Dispatcher.Invoke
:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
var worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// Suppose we've done some processing and need to notify the UI.
// We're on the background thread, so we can't manipulate the UI directly.
this.Dispatcher.Invoke(new Action(() =>
{
// This will actually run on the UI thread.
this.Label.Content = "hello from the background thread.";
}));
};
worker.RunWorkerAsync();
}
}
关于事件及其进行线程同步的神奇能力似乎存在很多混淆,所以请允许我咆哮一下。
事件是美化的多播代表。当您举起一个事件时,调用其调用列表中的每个代理,在引发该事件的线程上调用 。因此,如果您在自定义解析器类中创建一个事件只是为了从DoWork
处理程序中引发它,那么该事件的处理程序仍将在后台线程上执行,您仍然需要找到一种切换方法到UI同步上下文 - 通过在新事件的处理程序中执行一些Invoke
/ SynchronizationContext.Post
/ Send
魔术,或者通过调用/发布/发送实际的事件提升逻辑。 / p>
在UI线程上运行RunWorkerCompleted
和ProgressChanged
等开箱即用事件的处理程序的原因是这些事件实际上是为了您的方便,BackgroundWorker
的UI线程。是的,你可以通过捕获SynchronizationContext
然后在自定义解析器类中发布/发送事件提升逻辑来产生类似的行为。或者,如果您选择在后台线程上引发事件,则始终可以在WPF组件的处理程序中使用Dispatcher.Invoke
。