简体   繁体   English

从后台线程引发的事件更新UI

[英]Updating the UI from events raised by a background thread

My WPF application launches long running functions on background threads, typically via a button click/command, eg 我的WPF应用程序在后台线程上启动长时间运行的功能,通常通过按钮单击/命令,例如

StartCommand = new RelayCommand(async o => await StartAsync(), o => true);

...

private async Task StartAsync()
{
    await Task.Run(() => LongRunningFunction());
}

These long running functions raise various events to report progress to the user, update UI values, etc. The view-model handles such events and updates bound properties, eg: 这些长时间运行的函数引发各种事件以向用户报告进度,更新UI值等。视图模型处理此类事件并更新绑定属性,例如:

private void LongRunningProcessProgressChanged(object sender, ProgressEventArgs e)
{
    StatusMessageText = string.Format("Progress {0}%", e.Progress);
}

Most of the time this works fine but occasionally I get the usual exception about updating the UI from a background thread ("The calling thread cannot access this object because a different thread owns it")), so I have to wrap the code in a Dispatcher.Invoke(...) . 大部分时间这个工作正常,但偶尔我会得到关于从后台线程更新UI的常见例外(“调用线程无法访问此对象,因为不同的线程拥有它”)),所以我必须将代码包装在一个Dispatcher.Invoke(...) I haven't really spotted a pattern as to when I do or don't have to do this, so can anyone shed some light on it? 我还没有真正发现过我何时做或不做这种模式的模式,那么任何人都可以对它有所了解吗?

To be honest I'm surprised that the above code works at all. 说实话,我很惊讶上面的代码可以工作。 A breakpoint confirms that these event handlers run on a worker thread, not the UI thread, so why don't I see the exception all the time? 断点确认这些事件处理程序在工作线程上运行,而不是在UI线程上运行,那么为什么我不能一直看到异常呢? Is it something to do with the type of property being updated? 是否与正在更新的财产类型有关?

Edit People are proposing answers to a problem which I am already aware of. 编辑人们正在提出我已经知道的问题的答案。 Reading my question again I might have confused readers with the part "Most of the time this works fine but occasionally I get the usual exception". 再次阅读我的问题我可能会将读者与“大多数时候这种方法很好但偶尔会得到通常的例外”这一部分混淆。 I didn't mean that it's an intermittent problem - what I really meant was that some of my VMs don't have a problem updating bound properties within their respective progress event handler, while other VMs do. 我并不是说这是一个间歇性问题 - 我真正的意思是我的一些虚拟机在各自的进度事件处理程序中更新绑定属性时没有问题,而其他虚拟机则这样做。 I suspect it has something to do with the property type, eg updating a string property works but not an ObservableCollection. 我怀疑它与属性类型有关,例如更新字符串属性但不是ObservableCollection。

My question was more of a curiosity thing, ie why some bound properties are okay with being updated from ab/g thread while others aren't. 我的问题更多的是一个好奇的东西,即为什么一些绑定的属性可以从ab / g线程更新,而其他人不是。

Your code should always throw exceptions when you are updating UI from background threads. 从后台线程更新UI时,您的代码应始终抛出异常。 You don't always see the exceptions, because exceptions happens in background threads and remains unobserved . 您并不总是看到异常,因为异常发生在后台线程中并且仍未被观察到 For such exceptions 对于此类例外

.. TPL needs to backstop these exceptions and hold on to them until such time that they can be thrown again when the consuming code accesses the task. .. TPL需要支持这些异常并持续使用它们,直到消费代码访问任务时它们可以被再次抛出。

So you simply don't see the exceptions immediatly after they are thrown from background task. 因此,在从后台任务中抛出异常后,您根本看不到异常。


Update 更新

This answer sheds some light on common scenarios when accessing a control from another thread. 这个答案揭示了从另一个线程访问控件时的常见场景。 About the issue you described, it'a actually depending on type of property you are binding to as stated at MSDN forum : 关于您描述的问题,它实际上取决于您绑定到的属性类型,如MSDN论坛中所述

Data binding to primitive types is basically type safe, since the binding mechanism, internally, uses the dispatcher to marshal back to the UI thread for you. 对基本类型的数据绑定基本上是类型安全的,因为绑定机制在内部使用调度程序为您编组回UI线程。

However, you need to take care with collections. 但是,您需要注意收藏。 For example, ObservableCollection will not handle this, so you need to make changes to the collection on the UI thread. 例如,ObservableCollection将不处理此问题,因此您需要在UI线程上对集合进行更改。

In this blog post you can find some more details. 在这篇博客文章中,您可以找到更多详细信息。

The problem that you are facing is , you are trying to update an UI element which are at in the different thread and you could try this 您面临的问题是,您正在尝试更新位于不同线程中的UI元素,您可以尝试这样做

 App.Current.Dispatcher.Invoke(new Action(() =>
            {
               // your code
            }));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM