Monday, July 25, 2011

WPF: Build Responsive UI (Threading)

I am going to explain how to build responsive user Interface. Implementing threading in WPF.. and this post may help the people who is getting "The calling thread cannot access this object because a different thread owns it" exception

The most frequent problem or situation wpf developers face is Blocked UI.
This solution also works for "calling thread cannot access this object because a different thread owns it" exception.
when we are pooling some data to/from the server wpf UI will get hangs which will question the capability of WPF.

here i will try to explain how to use thread to overcome this.

In the following code i am creating new instance for task factory and starting a new task.
so all the code we pass in the factory will run in the background..

TaskFactory tFactory = new TaskFactory();
tFactory.StartNew(() =>
{
// DO YOUR SERVER CALLS or SOMETHING which you want to run in the background
});


After looking the above the first question comes in mind is Can i update UI from this task or thread.?
and the answer is NO You can't update UI from the background thread.
for updating UI you have to take the control to the Dispatcher thread.
some thing like this.

TaskFactory tFactory = new TaskFactory();
tFactory.StartNew(() =>
{
// DO YOUR SERVER CALLS or SOMETHING which you want to run in the background
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
// UI Related stuff
});
});


We learned how to execute a task in background and how to update UI from that thread.

Now let us discuss about how to execute the task and some other code synchronously.

Consider this scenario.
we have to get some data from the server and after that we have play with data.
means our next action is completely dependent on the callback or completion of Task.

in this case we can use task factory ContinueWith method like following

TaskFactory tFactory = new TaskFactory();
tFactory.StartNew(() =>
{
// DO YOUR SERVER CALLS or SOMETHING which you want to run in the background
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
// UI Related stuff
});
}).ContinueWith(t =>
{
if (t.IsFaulted)
{
throw t.Exception;
}
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
// do some thig after task completion
});
});


Now i am putting all the three scenario in one place with simple example

ObservableCollection images = new ObservableCollection();
TaskFactory tFactory = new TaskFactory();
tFactory.StartNew(() =>
{
for (int i = 0; i < 50; i++)
{
//GET IMAGE Path FROM SERVER
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
// UPDATE PROGRESS BAR IN UI
});

images.Add(("");
}

}).ContinueWith(t =>
{
if (t.IsFaulted)
{
// EXCEPTION IF THREAD IS FAULT
throw t.Exception;
}
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
//PROCESS IMAGES AND DISPLAY
});
});


AppDomain.CurrentDomain.SetData("SQLServerCompactEditionUnderWebHosting", true);

No comments:

Post a Comment