Sunday, May 29, 2011

Using a Background Process in WPF

A simple trick for avoiding unresponsiveness of user interfaces in WPF


WPF applications often need to call time consuming methods or processes, and the time consuming methods or processes can be huge time consuming calculations or perhaps a Web Service call. In the case of WPF, specially XBAP (WPF browser application) which runs on a remote client machine's browser, this sort of calls can make your user interface unresponsive. I have working on WPF for more than four months. A lot of things have come my way and been dealt with nicely. Well in this post, I am going to share a simple trick for avoiding unresponsiveness of user interfaces. We have already used this technique in many cases in our application, but this is for WPF.

The "Thread & Invoke"
The BackgroundWorker is a very smooth and useful tool for our purpose of making more responsive UIs (user interfaces). Before discussing more about the BackgroundWorker, let's take a flash back of the legacy technique (which is pretty smart) of making more responsive UIs. Previously, I used threading for implementing such kinds of UI smoothness. What I did is create a background thread and call the expansive operation on that thread. When the job is finished, I use the MethodInvoker method to let know the UI thread that the job is finished. And this model is called the asynchronous model. And this is quite a smart model and the rest of the models are based on this approach. Here is a quick code snippet for demonstrating the technique.

Collapse | Copy Code
//first start the method with tread
System.Threading.ThreadStart ts =
new System.Threading.ThreadStart(ExpansiveMethod);
System.Threading.Thread t = new System.Threading.Thread(ts);
t.Start();
protected void ExpansiveMethod()
{
//Very expansive call will go here...
//after the job is finished call method to update ui
MethodInvoker updaterMI = new MethodInvoker(UpdateChange);
this.BeginInvoke(UpdateChange);
}
protected void UpdateChange()
{
//again back to main ui thread
}The "Background Worker"
Okay, it's time to use the BackgroundWorker. An amazing thing about the BackgroundWorker is, it's simple to use. First, let's see what a background worker is. BackgroundWorker is a class under System.ComponentModel which executes an operation on a separate thread. I was introduced in .NET Framework 2.0. Things are pretty simple, just like thread. All you have to do is instantiate a BackgroundWorker and subscribe to its events, and call the RunWorkerAsync() method. Let's see a code snippet. Since we are programmers, we understand code better.

Collapse | Copy Code
void MyMethodToCallExpansiveOperation()
{
//Call method to show wait screen
BackgroundWorker workertranaction = new BackgroundWorker();
workertranaction.DoWork +=
new DoWorkEventHandler(workertranaction_DoWork);
workertranaction.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(workertranaction_RunWorkerCompleted);
workertranaction.RunWorkerAsync();
}
void workertranaction_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Call method to hide wait screen
}
void workertranaction_DoWork(object sender, DoWorkEventArgs e)
{
//My Expansive call will go here...
}As you can see, in the above code, I have subscribed to two events: DoWork (which is the main function) and RunWorkerCompleted. In the DoWork event handler, we will put our expansive time consuming operations; as the name implies, the RunWorkerCompleted event is fired when the work is finished. BackgroundWorker also has a ProgressChanged event which is used to let the main UI thread know how much work is completed.

The "Dispatcher"
In a few cases, the BackgroundWorker needs to access the main UI thread. In WPF, we can use Dispatcher which is a class of System.Windows.Threading and a delegate to access the main thread. First of all, we have to declare a delegate for our candidate methods, and then use the delegate to call the method using Dispatcher. Dispatcher has different thread priorities and you can choose a priority from the DispatcherPriority enum. Send has the highest priority in DispatcherPriority.

Collapse | Copy Code
//delegate for our method of type void
public delegate void Process();
//and then use the dispatcher to call the method.
Process del = new Process(UpdateMyUI);
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, del);
void UpdateMyUI()
{
//get back to main UI thread
}

No comments:

Post a Comment