如果您绝对必须在处理程序中安排 UI 线程上的工作DoWork
,最简单的方法是使用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
开箱即用事件的处理程序(例如RunWorkerCompleted
和在 UI 线程上运行)的原因是,为了您的方便ProgressChanged
,这些事件实际上是由 UI 线程在 UI 线程上引发的。是的,您可以通过在自定义解析器类中捕获然后发布/发送事件引发逻辑BackgroundWorker
来产生类似的行为。SynchronizationContext
或者,如果您选择在后台线程上引发事件,则始终可以Dispatcher.Invoke
在 WPF 组件的处理程序内部使用。