Reputation: 4322
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.
Upvotes: 18
Views: 74105
Reputation: 1114
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...
Upvotes: 1
Reputation: 25650
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
Upvotes: 4
Reputation: 485
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?
Upvotes: 4
Reputation: 15247
I honestly think the BackgroundWorker
is the most elegant solution for this. I cannot think of a simpler way to do it.
Upvotes: 25
Reputation: 3843
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.
Upvotes: 2