Proper use of threads can greatly increase the responsiveness of your
WPF applications. Unfortunately, you can't update any UI controls from a
thread that doesn't own the control. In .NET 2.0, you used
Control.Invoke(). Now, we've got something similar but more powerful -
the Dispatcher. This tutorial will explain what the Dispatcher is and
how to use it.
First of all, you need to know where the Dispatcher lives. Every Visual (Button, Textbox, Combobox, etc.) inherits from DispacterObject. This object is what allows you to get a hold of the UI thread's Dispatcher.
The Dispatcher is why you can't directly update controls from outside threads. Anytime you update a Visual, the work is sent through the Dispatcher to the UI thread. The control itself can only be touched by it's owning thread. If you try to do anything with a control from another thread, you'll get a runtime exception. Here's an example that demonstrates this:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "A Checkbox";
System.Threading.Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
myCheckBox.IsChecked = true;
}
));
thread.Start();
}
}
This code creates a Checkbox, then creates a thread which tries to change the checked state. This will fail because the Checkbox was created on a different thread than the one trying to modify it. If you run this code, you'll end up with this exception:
The calling thread cannot access this object because a different thread owns it.
So the question is, how do you update the Checkbox from this thread? Fortunately, the Dispatcher gives us the ability to Invoke onto its thread. Invoking probably looks really familiar if you've programmed in .NET 2.0. We actually have an in-depth tutorial on invoking that you might want to read. Below is code using the Dispatcher that will run and update the Checkbox without throwing an exception.
public Window1()
{
InitializeComponent();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "A Checkbox";
System.Threading.Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
myCheckBox.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
}
));
thread.Start();
}
Now we've introduced the Dispatcher object. The call to Invoke needs to take a few pieces of information. First is the priority you'd like your work executed with. Next is the delegate that contains the work you actually want to do. If your delegate takes parameters, the Invoke call will also accept an Object or Object[] to pass into the delegate function. It will also accept a timespan that limits the amount of time the Invoke call will wait to execute your code.
The call to Invoke will block until your function has been executed. Depending on the priority you've set, this might take a while. The Dispatcher also has the ability to invoke code asynchronously using BeginInvoke. Let's look at the same example using BeginInvoke.
public Window1()
{
InitializeComponent();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "A Checkbox";
System.Threading.Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
System.Windows.Threading.DispatcherOperation
dispatcherOp = myCheckBox.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
dispatcherOp.Completed += new EventHandler(dispatcherOp_Completed);
}
));
thread.Start();
}
void dispatcherOp_Completed(object sender, EventArgs e)
{
Console.WriteLine("The checkbox has finished being updated!");
}
BeginInvoke takes many of the same arguments as Invoke, but now returns a DispatcherOperation that lets you keep track of the progress of your function. In this case, I simply hooked the Completed event that notifies me when the work has been completed. The DispatcherOperation object also lets you control the Dispatcher by changing its priority or aborting it all together.
As I mentioned above, we can limit the amount of time an Invoke is allowed to take my passing the Invoke call a TimeSpan structure. Here's an example that will wait 1 second for the queued function to complete:
public Window1()
{
InitializeComponent();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "A Checkbox";
System.Threading.Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
myCheckBox.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.SystemIdle,
TimeSpan.FromSeconds(1),
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
}
));
thread.Start();
}
Unfortunately, there's no way to determine if the invoke finished or timed out from the outside. You can always put code inside the invoked function to determine if it executed.
All right, so we've got our dispatchers working and code is being executed where it's supposed to be. Invokes, however, are kind of an expensive process. What happens when your controls are being updated from both external threads and the main UI thread. How do you know if you're supposed to use Invoke? The Dispatcher object provides a function that tells you whether or not you have to use Invoke. In WinForms you call InvokeRequired on the actual control. In WPF, you call CheckAccess() on the Dispatcher object. CheckAccess() returns a boolean indicating whether or not you can modify the control without Invoking.
if (!myCheckBox.Dispatcher.CheckAccess())
{
myCheckBox.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
}
else
{
myCheckBox.IsChecked = true;
}
So now, before I invoke, I check to see if I even need to. If I do, I invoke the function, if I don't, I simply update the control directly.
As you can see, the Dispatcher provides a great deal of flexibility over the WinForms Invoke. There's a lot about WPF's dispatching model that we didn't touch in this tutorial. The System.Windows.Threading namespace contains a lot more useful objects that extends the power of the dispatcher even further.
I keep on getting an error,
Error 3 Using the generic type 'System.Action' requires '1' type arguments C:\WPF1\Window1.xaml.cs 281 23 WPF1
Very good. Thanks
Very good tutorial, thanks! I have a problem: i open a WPF Window from an existing WinForm project, and the code loads database data (with progress bar update), but loading freezes apparition of splash window (2000ms). So i tried a BeginInvoke and thread creation to show splash screen in another thread, but it doesn't work. It seems that the "main" thread must be free to animate WPF. I can't move database loading code into another thread because there's winform interaction and even with an "STA thread", it raises an error "A control in a thread cannot be parent of a control in another thread". If you have an idea... Thanks ! :-)
Hi Emmanuel, I am facing the similar problem. Did you get any help for your problem. If so, please share that with me. Thanks for your help.
Hi, you should load the database data on a background worker thread and report the progress to the gui thread. You can use a Dispatcher.Invoke to call the 'UpdateProgressBarMethod'
Greetz, JvanLangen
Nice one! Thanks
Great explanation of what for me, is a difficult subject to grasp. Thank you for creating this tutorial. I was able to resolve my problem and look like a pro thanks to you. Very well done indeed!!!
Excellent tutorial - I appreciate the time you took to explain that.
Just a couple of comments...
A "using System.Windows.Threading" might have made the code a lot easier to read.
Anonymous methods tidy up the code but perhaps make it a little less readable
But perhaps that's just me - again, Thanks
Nice work - thanks!
Thanks bro ;)
Thanks man, but I have a question! I am trying to keep on refreshing my TextBLock independent of the main thread! i am trying to do this, this way:
The problem is that it is not refreshing! I would appreciate any help, thanks :)
You've got a tight loop here with no sleep. You're basically consuming the entire CPU, leaving nothing to actually redraw the screen. Try putting a sleep after the invoke and see what happens. Other than that, the code looks good.
Hi,
I am facing similar problem as of above. I dont have tight loop. I am just displayng the message ones. But still my UI is not refresning.
Thanks a lot, it worked like a charm :)
http://msdn.microsoft.com/en-us/library/8xs8549b.aspx
This url refers to the backgroundworker class, part of the componentmodel namespace.
This is a stable solution to the problem of refreshing WPF controls which running and cancelling threads.
With regards,
Richard
It helps me.
I'm no expert but I wanted to call a multiple parameter delegate. I created the TextBox object directly in the xaml file and the following code works for .net 3.5.
.NET has several multi-parameter delegates that can be used instead of declaring your own. Check out Action. There are action delegates that accept anywhere from 1 to 10 parameters.
Not sure I get this. This seems to block my main UI thread for 10 seconds. Should not the Thread.Sleep be executing on another thread?
System.Threading.Thread thread = new System.Threading.Thread( new System.Threading.ThreadStart( delegate() { System.Windows.Threading.DispatcherOperation dispatcherOp = this.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, new Action( delegate() { Thread.Sleep(10000); } )); } ));
thread.Start();
The Thread.Sleep is not being executed on the new thread. Since you've invoked it onto the dispatcher, the dispatcher is the one doing the work. Since the dispatcher is also responsible for drawing the UI, doing this will block the UI.
BeginInvoke, underneath, does the same thing has Invoke. The difference is that the calling code doesn't wait for the dispatcher to actually complete the work.
Thanks for the response. I am using Thread.Sleep just as a sample. How could I execute code that can access the UI without blocking the main thread? Do I need to create a background worker and that calls Invoke on the window?
A thread will work fine. You just have to do the bulk of the work directly in the thread and only invoke to update the GUI.
.NET also provides a BackgroundWorker class that might help you out.
I have a window that takes quite a while to load. I was hoping to just toss the instantiation/creation off to another thread and not have the UI block. Is that possible?
Thank you very much, this helps me a lot....
This article is really awesome! Thanks a lot man you saved my time.
Hi, I am new to WPF and facing one difficulty. I have a complete GUI with lots of menus and buttons and heavy data is loaded and transfer to the database. There is one button on the from "Export to PDF" which perform some action like convert text to byte array and then append to PDF etc my problem is that when user clicks the button the entire application become irresponsive. Is there any way to do the same thing in background with out making entire application idle. Waiting for your reply
Yes, use the BackgroundWorker Class.
Great article, well done. This solved my problem.
Really helpful !! Thank you very much :)
Excellent article... really helpful
Hi, I want to know if there is any difference between WPF - Dispatcher.Invoke and Winform - Control.Invoke (not the syntactical difference)?
Hello! I don't know how in wpf, but in Silverlight 4 the method Dispatcher.CheckAccess() is invisible for Intellisense, because it has been marked with a [EditorBrowsable(EditorBrowsableState.Never)] attribute. Probably, Microsoft doesn't want developers to use Dispatcher.CheckAccess(). Taking into account that all controls, including page, are created inside the UI thread, inside a page constructor the property Thread.CurrentThread.ManagedThreadId is the required UI thread identifier. We can use this fact it in the next way:
I've described it in details in my post in blog here http://dotnetfollower.com/wordpress/2011/07/silverlight-for-windows-phone-7-how-to-check-if-the-current-thread-is-ui-one/
Thank you! .Net Follower (http://dotnetfollower.com)
i think i'm gonna find a solution from the mess i'm trapped in.. Thank you
This is what i was looking for (didn't read through everything, but just the first sample was what i needed.
Many Thanks
Hi There,
I have a WPF Class in which i am hosting windows Controls. I am facing the problem when i am calling a method in WPF class from the Windows Control to create a tab.I get a message saying "The calling thread cannot access this object because a different thread owns it".
Can you pleas tell me what might be the Problem.
thanks.
THIS ITS SIMPLE .
Lets say you have Combobox1 in your application.
when you want to add new items to it, just SIMPLE use:
Me.Combobox1.Dispacher.Invoke(New Action(Sub() /*Add here*/ End Sub))
new article
we also need in vb.net
Good article, it really helped me, thanks.
great article.....helped me a lot to understand nicely.
thx.
Good Article for sure.
Thanks! It helped me here ;)
Hi,
I am not quite sure what causes "The calling thread cannot access this object because a different thread owns it." error on the following snippet:
_form.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate { _form.ShowDialog(); _form = null; }));
On first load of the app it seems the _form.ShowDialog() is working. But when I hide it and then fires up the method that contains the codes above, then it raises that error.
Hope you can help me. Thanks.
Thanks
Awesome tutorial!
Thanks ...... smoothy explained.......!
hi,i have created a page in wpf which has a button on it, on clicking that button one window is opened. I want to implement a functionality using which the another window on the same button click is not generated untill the firs window is closed..can anybody help me??
Can you make the window that's opened modal? Use ShowDialog instead of Show.
hi.thanks for your help..I tried that.. but it is not working..I mean when i click on the button on page, windows are getting generated on every click..without checking whether previously opened window is closed or not..This is what I tried.. private void button_Click(object sender, RoutedEventArgs e) { Window w1 = new Window1();
w1.ShowDialog(); }
Very good article Thanks !!!
Cheer's
Muchas gracias por el post, sencillo y conciso, muy bueno en realidad, me ha servido de mucho, GRACIAS.. saludos desde CUBA
Thanks for this, it made for an easy solution on an issue I had.
Thx! Very useful