我在WPF中注意到,当尝试从后台线程更新UI时(我知道你不应该这样做 - 只是玩弄东西),有时它会抛出InvalidOperationException,有时它只是什么也没做。当我不正确地尝试从异步WCF调用启动的后台线程更新UI时,我首先注意到这一点(使用Begin / End,而不是自动编组到UI线程的事件模型)。
例如,假设我有一个带有按钮和复选框的简单表单。此代码将抛出一个InvalidOperationException(“调用线程无法访问此对象,因为不同的线程拥有它。”)每次:
private void button1_Click(object sender, RoutedEventArgs e)
{
new Thread(() => checkBox1.IsChecked = true).Start();
}
现在,采用相同的形式,并在某处向一个简单的WCF服务添加一个标准的服务引用。然后试试这个:
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
}
我的理解是在该回调方法中调用Dispatcher.VerifyAccess()应该抛出,就像试图操纵checkBox1上的任何东西一样。相反,没有任何反应 - UI中的复选框不会被检查,也不会抛出任何异常。有谁知道为什么会这样?
答案 0 :(得分:1)
调用回调方法的Framework代码是否可能吞噬异常?在回调中放置try/catch
以查看是否抛出异常。如果是这样,那么您就知道框架正在吞噬异常。
答案 1 :(得分:0)
通常,大多数GUI环境都不支持(以及那些甚至不鼓励的人)从操作不同线程的小部件。通常,只有一个线程处理给定的GUI。大多数GUI工具包提供了一种机制,您可以通过该机制调用gui线程上的“任务”,从而安全地操作小部件。对于你希望做的小部件的每个tweek来创建一个新的任务,这是一个单调乏味的练习,但实际上没有其他选择。
对于C#和wpf(已经有一段时间了)我相信机制围绕着:
widget.Dispatcher.BeginInvoke(delegate, args[])
因此,对于您希望执行的小部件的每个tweek,您创建一个委托来执行该tweek,然后通过小部件Dispatcher(处理在小部件gui线程上调度事件的对象)调用它。