[英]Updating the UI from events raised by a background thread
我的WPF应用程序在后台线程上启动长时间运行的功能,通常通过按钮单击/命令,例如
StartCommand = new RelayCommand(async o => await StartAsync(), o => true);
...
private async Task StartAsync()
{
await Task.Run(() => LongRunningFunction());
}
这些长时间运行的函数引发各种事件以向用户报告进度,更新UI值等。视图模型处理此类事件并更新绑定属性,例如:
private void LongRunningProcessProgressChanged(object sender, ProgressEventArgs e)
{
StatusMessageText = string.Format("Progress {0}%", e.Progress);
}
大部分时间这个工作正常,但偶尔我会得到关于从后台线程更新UI的常见例外(“调用线程无法访问此对象,因为不同的线程拥有它”)),所以我必须将代码包装在一个Dispatcher.Invoke(...)
。 我还没有真正发现过我何时做或不做这种模式的模式,那么任何人都可以对它有所了解吗?
说实话,我很惊讶上面的代码可以工作。 断点确认这些事件处理程序在工作线程上运行,而不是在UI线程上运行,那么为什么我不能一直看到异常呢? 是否与正在更新的财产类型有关?
编辑人们正在提出我已经知道的问题的答案。 再次阅读我的问题我可能会将读者与“大多数时候这种方法很好但偶尔会得到通常的例外”这一部分混淆。 我并不是说这是一个间歇性问题 - 我真正的意思是我的一些虚拟机在各自的进度事件处理程序中更新绑定属性时没有问题,而其他虚拟机则这样做。 我怀疑它与属性类型有关,例如更新字符串属性但不是ObservableCollection。
我的问题更多的是一个好奇的东西,即为什么一些绑定的属性可以从ab / g线程更新,而其他人不是。
从后台线程更新UI时,您的代码应始终抛出异常。 您并不总是看到异常,因为异常发生在后台线程中并且仍未被观察到 。 对于此类例外
.. TPL需要支持这些异常并持续使用它们,直到消费代码访问任务时它们可以被再次抛出。
因此,在从后台任务中抛出异常后,您根本看不到异常。
这个答案揭示了从另一个线程访问控件时的常见场景。 关于您描述的问题,它实际上取决于您绑定到的属性类型,如MSDN论坛中所述 :
对基本类型的数据绑定基本上是类型安全的,因为绑定机制在内部使用调度程序为您编组回UI线程。
但是,您需要注意收藏。 例如,ObservableCollection将不处理此问题,因此您需要在UI线程上对集合进行更改。
在这篇博客文章中,您可以找到更多详细信息。
您面临的问题是,您正在尝试更新位于不同线程中的UI元素,您可以尝试这样做
App.Current.Dispatcher.Invoke(new Action(() =>
{
// your code
}));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.