Question

I have a parser class for large CSV files. The parse method's work of reading through the large files line by line is done in a 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. 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. 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?

Was it helpful?

Solution

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:

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.

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. 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. 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top