Trying to update UI from background thread: Why does it sometimes throw and sometimes just do nothing?
I've noticed in WPF that when trying to update the UI from a background thread (I know that you're not supposed to do this - just playing around with things) that sometimes it throws an InvalidOperationException, and sometimes it just does nothing. I first noticed this when I was improperly trying to update the UI from a background thread started by an async WCF call (using Begin/End, not the eventing model that automatically marshals to the UI thread).
For example, let's say I've got a simple form with a button and a checkbox. This code will throw an InvalidOperationException ("The calling thread cannot access this object because a different thread owns it.") every time:
private void button1_Click(object sender, RoutedEventArgs e)
{
new Thread(() => checkBox1.IsChecked = true).Start();
}
Now, take the same form, and add a bog-standard service reference to a simple WCF service somewhere. Then try this:
private void button1_Click(object sender, RoutedEventArgs e)
{
var client = new MyServiceClient();
//Note the use of Begin/End as opposed to the eventing model - Callback
//will not be called on the UI thread, but a worker thread. I have
//verified this through the debugger thread list and by checking the
//result of Dispatcher.CheckAccess() in the callback.
client.BeginMyServiceMethod("MyArgument", Callback, null);
}
private void Callback(IAsyncResult result)
{
//If I call Dispatcher.CheckAccess() here, it returns false,
//but if I call Dispatcher.VerifyAccess() it does not throw!
checkBox1.IsChecked = true; // no exception, no effect
}
My understanding is that a call to Dispatcher.VerifyAccess() in that callback method should throw, as should trying to manipulate anything on checkBox1. Instead, nothing happens - the checkbox in the UI does not become checked开发者_JAVA百科, and no exception is thrown. Does anyone know why this is the case?
Is it possible that the Framework code that's making the call to the callback method is swallowing exceptions? Put a try/catch
in the callback to see if exceptions are being thrown. If so, then you know that the Framework is swallowing exceptions.
In general, most GUI environments do not support(and those that do even discourage) you from manipulating widgets from different threads. Typically there is only 1 thread handling a given GUI. Most GUI kits supply a mechanism by which you can invoke a 'task' on the gui thread and thus manipulate the widgets safely. It gets to be an exercise in tedium to create a new task for each tweek of a widget that you wish to do, but there really isn't an alternative.
For C# and wpf(it's been a little while) I believe the mechanism revolves around:
widget.Dispatcher.BeginInvoke(delegate, args[])
So for each tweek of a widget you wish to do, you create a delegate to perform that tweek, and then invoke it through the widgets Dispatcher(the object that handles the dispatching of events on the widgets gui thread).
References
BeginInvoke
精彩评论