简体   繁体   English

BackgroundWorker进程需要引发要在主(UI)线程上处理的自定义事件

[英]BackgroundWorker process needs to raise custom events to be handled on the main (UI) thread

I have a parser class for large CSV files. 我有一个用于大型CSV文件的解析器类。 The parse method's work of reading through the large files line by line is done in a backgroundWorker. 解析方法的逐行读取大文件的工作是在backgroundWorker中完成的。 The percentage complete information is passed to the UI thread using the backgroundWorker.ReportProgress method, so that a progress bar on my form can do its thing. 使用backgroundWorker.ReportProgress方法将完成百分比信息传递给UI线程,以便表单上的进度条可以完成其工作。 That's working fine. 很好

However, I would also like to raise a custom event that sends back to the UI (WPF) a list of fieldnames taken from the first line of the CSV file, so that they can be placed in a dropdown list. 但是,我还想提出一个自定义事件,该事件将从CSV文件的第一行获取的字段名称列表发送回UI(WPF),以便可以将它们放置在下拉列表中。 I would also like to inform the user via an event if the parser should encounter malformed lines or other roadblocks. 我还想通过事件通知用户解析器是否遇到格式错误的行或其他障碍。

Can my parser process executing in the background simply raise an event? 我在后台执行的解析器进程可以引发一个事件吗? Or must the SynchronizationContext.Current be passed from the main UI thread to my parser class, which would then use the Post method? 还是必须将SynchronizationContext.Current从主UI线程传递到我的解析器类,然后再使用Post方法?

If you absolutely must schedule work on the UI thread from within your DoWork handler, the simplest way to do this is by using Dispatcher.Invoke : 如果您绝对必须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();
    }
}

There seems to be a lot of confusion about events and their magical ability to do thread synchronization, so allow me to rant for a bit. 关于事件及其执行线程同步的神奇能力,似乎存在很多困惑,因此请允许我大声疾呼。

Events are glorified multicast delegates. 事件是美化的多播委托。 When you raise an event, each delegate in its invocation list is called, on the thread which raised the event . 引发事件时,将在引发事件的线程上调用其调用列表中的每个委托。 Therefore, if you create an event in your custom parser class just to have it raised from the DoWork handler, the handlers for the event will still execute on the background thread, and you will still need to find a way to switch to the UI synchronization context - either by performing some Invoke / SynchronizationContext.Post / Send magic inside the handler of the new event, OR by Invoking/Posting/Sending the actual event raise logic. 因此,如果仅在自定义解析器类中创建事件以使其从DoWork处理程序中引发,则该事件的处理程序仍将在后台线程上执行,并且仍将需要找到一种方法来切换到UI同步上下文-通过在新事件的处理程序内执行一些Invoke / SynchronizationContext.Post / Send魔术,或者通过调用/发布/发送实际事件引发逻辑。

The reason that handlers for out-of-the-box events such as RunWorkerCompleted and ProgressChanged run on the UI thread is that these events are actually raised on the UI thread by the BackgroundWorker for your convenience. 在UI线程上运行开箱即用事件(例如RunWorkerCompletedProgressChanged处理程序的原因是,为方便起见, BackgroundWorker 实际上在UI线程上引发了这些事件。 And yes, you can produce similar behaviour by capturing the SynchronizationContext and then Posting/Sending the event raise logic in your custom parser class to it. 是的,您可以通过捕获SynchronizationContext ,然后在自定义解析器类中向其发布/发送事件引发逻辑来产生类似的行为。 Alternatively, if you choose to raise the event on the background thread, you can always use Dispatcher.Invoke inside its handler in your WPF component. 另外,如果选择在后台线程上引发事件,则始终可以在WPF组件的处理程序内部使用Dispatcher.Invoke

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM