Thursday, May 8, 2008

Using Background Process in WPF

WPF applications often needs to call time consuming methods or processes, the time consuming methods or processes can be, huge time consuming calculation or perhaps a web service call. In case of WPF specially XBAP (wpf browser application) which run on remote client machines browser, this sort of calls can make your user interface unresponsive.  I am working with WPF for more than four month. Lot of things come in to way and dealt with nicely. Well in this post I am going to share a simple trick for avoiding unresponsiveness of user interface. We have already used this technique in many cases in our application, but this is for WPF.

The "Thread & Invoke"

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

//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, its time to use the BackgroundWorker. An amazing thing about background worker is, its simple to use.  First, lets see what a background worker is. "BackgroundWorker" is a class under "System.ComponentModel" which executes an operation on a separate thread. Which is introduced from dot net framework 2.0. Things are again pretty simple just like tread, All you have to do is instantiate a BackgroundWorker and subscribe its events, and call the "RunWorkerAsync()" method. Lets put a code snippet. Since we are programmers we understand code better.




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 above code I have subscribed two event "DoWork" (which is the main function) and "RunWorkerCompleted", In dowork event handler we will put our expansive time consuming operations, as the name imply's RunWorkerCompleted event is fired when the work is finished . BackgroundWorker also has "ProgressChanged" event which is used to let the main UI thread know how much work is completed.


The "Dispatcher"


In 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 few thread priority and you can use various priority from DispatcherPriority enum. "Send" has the highest priority in DispatcherPriority.




//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
}



For more reading about this Asynchronous Programming please visit the references.


Reference