WPF Dispatcher.BeginInvoke and UI/Background Threads

72,800

Solution 1

I honestly think the BackgroundWorker is the most elegant solution for this. I cannot think of a simpler way to do it.

Solution 2

Charlie's answer is what you are looking for, really.

However, if it's possible you might look at whether or not you can parcel up your work so that the individual units of work are small and don't affect the UI as much. This would allow you to just use the Dispatcher directly. There is a good example of this on the WPF Threading page: https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx

Solution 3

Me too don't like BackgroundWorker. A simple alternative can be something like:

using System;
using System.Threading;
using System.Windows;

namespace Sample
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
             base.OnSourceInitialized(e);
             longWorkTextBox.Text = "Ready For Work!";
       }

        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            new Thread(Work).Start();
        }

        void Work()
        {
            longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; }));
            Console.WriteLine("Starting Work Action");
            int i = int.MaxValue;
            while (i > 0)
                i--;
            Console.WriteLine("Ending Work Action");
            longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; }));
        }
    }
}

Easy, not?

Solution 4

As its name indicates it will execute in the Background so you don't need to instantiate it with the Dispatcher. Plus if you want this code to run into a WP7 the BeginInvoke does not get the background parameter.

My recommendation is to create the BackgroundWorker as:

BackgroundWorker worker = new BackgroundWorker;

And then create the handlers for the events:

worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork +=new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged);

And finally you call:

bkwkPlayingLoop.RunWorkerAsync();

It is a big temptation to use the Dispatcher from inside the DoWork but instead call worker.ReportProgress() and handle the UI from there. You will otherwise face some inconsistencies with the firing of termination events.

Solution 5

Tasks are easier to use than Background workers, do more things, have fewer issues and were pretty much created so Background Workers didn't need to be used anymore...

Share:
72,800
Matthew Ruston
Author by

Matthew Ruston

Software engineer, DBA, video gamer, dad.

Updated on July 09, 2022

Comments

  • Matthew Ruston
    Matthew Ruston almost 2 years

    I think I need some clarifications regarding WPFs Dispatcher.Invoke and Dispatcher.BeginInvoke usage.

    Suppose I have some long running 'work' code like such that is invoked on the press of a button in a simple WPF application:

    longWorkTextBox.Text = "Ready For Work!";
    Action workAction = delegate
        {
        Console.WriteLine("Starting Work Action");
        int i = int.MaxValue;
        while (i > 0)
            i--;
        Console.WriteLine("Ending Work Action");
        longWorkTextBox.Text = "Work Complete";
        };
    longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);
    

    This code is locking up my user interface while the workAction is being performed. This is because Dispatcher invokes always run on the UI thread, right?

    Assuming this, what is the best practice for configuring my dispatcher to execute the workAction in a separate thread from my UI? I know I can add a BackgroundWorker to my workAction to prevent my UI from locking as such:

    longWorkTextBox.Text = "Ready For Work!";
    Action workAction = delegate
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            Console.WriteLine("Starting Slow Work");
            int i = int.MaxValue;
            while (i > 0)
            i--;
            Console.WriteLine("Ending Work Action");
        };
        worker.RunWorkerCompleted += delegate
        {
            longWorkTextBox.Text = "Work Complete";
        };
        worker.RunWorkerAsync();
     };
     longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);
    

    Is there any more elegant ways of doing this besides using the BackgroundWorker? I've always heard that the BackgroundWorker is quirky, so I am curious to know of some alternatives.