WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI.
I can’t count the number of times someone has asked me about running a time consuming task on a separate thread, but at the same time show a progress dialog with up-to-the-second percentage updates being displayed to the user. Multithreading can be confusing at first, but if you just take it one step at a time, it really isn’t all that bad. I am going to show you how to create a multithreaded application that shows a progress dialog which shows real time progress to the user.
First lets start with how to get started with multithreading in WPF. If you don’t want to read how this actually works and just want to get the source and start playing ,here is the Source Code.
Your WPF application may need to perform intensive tasks that consume large amounts of time. Executing these tasks on a separate thread allows you to maintain the responsiveness of the UI while the task is performed. Enter the BackgroundWorker. The BackgroundWorker is the recommended way to run time consuming tasks on a separate, dedicated thread, leaving the UI responsive.
Running a Background Process
The RunWorkerAsync method starts the execution of the background process by raising the DoWork event. The code in the DoWork event handler is executed on a separate thread.
Providing Parameters to the Process
Your background process may required one or more parameters, such as the address of a file to download. You can provide a parameter in the RunWorkerAsync method that will be available as the Argument property in the DoWork event handler.
Returning a Value from the Process
You might want to return a value from a background process, such as a result from a calculation. You can return a value by setting the Result property of the DoWorkEventArgs in the DoWork event handler. Then, this value can be retrieved from the Result property of the RunWorkerCompletedEventArgs parameter int the RunWorkerCompleted event handler.
Cancelling the Background Process
You may want to allow the user to cancel a long running process. In order to support this you must first set the WorkerSupportsCancellation to true. You call the CancelAsync mtehod to attempt to cancel the process. When you call the CancelAsync method it sets the CancellationPending property of the BackgroundWorker to true. Then you must check for the value of the CancellationPending property in the DoWork event handler, and if it is true, set the Cancel property of the DoWorkEventArgs parameter to true.
Reporting the Progress of a Background Process
You can report the progress of a background process back to the primary thread by calling the ReportProgress method. This method raises the ProgressChanged event and allows you to pass a parameter that indicated the percentage of progress that has been completed. Make sure you set the WorkerReportsProgess property of the BackgroundWorker to true.
Using the Dispatcher to Access Controls on Another Thread
At times, you may want to change the user interface from a worker thread. For example, you may want to enable or disable buttons, or show a modal ProgressBar that provides more detailed progress information than is allowed by the ReportProgress method. The WPF threading model provides the Dispatcher class for cross thread calls. By using the Dispatcher, you can safely update your user interface from background worker threads.
You can get a reference to the Dispacther object for a UI element from its Dispatcher property.
System.Windows.Threading.Dispatcher aDisp = Button1.Dispatcher;
Dispatcher provides two main methods that you will use; Invoke and BeginInvoke. Both methods allows you to call a method safely on the UI thread. The BeginInvoke method allows you to call a method asynchronously, and the Invoke method allows you to call a method synchronously.
Putting It All Together
Lets say I have a time consuming task in my application. I want to execute this time consuming task on a background thread, but I also want to show a modal progress dialog that shows a message and the percent of the completed task. I also want to allow the user to cancel the process at anytime. So the first thing I need to do is create my progress dialog window that has a label to display my percent completed, a progress bar to graphically show the progress, and a cancel button so the user can cancel the process.
I need to expose two properties; ProgressText to set my label, and ProgressValue to set the progress bar value. I also need to expose an event so when the user hits the cancel button the process will stop.
Now on my main application I need a button to execute this long running process and show my progress dialog.
Now lets code the background worker.
That’s it. See, I told you it wasn’t that bad. Now that you have a basic understanding of multithreading, try to see how creative you can get on implementing it in your WPF application.