确保在MVVM WPF应用程序中的UI线程上调用OnPropertyChanged()

在我正在使用MVVM模式编写的WPF应用程序中,我有一个后台进程来完成这件事,但是需要从UI中获取状态更新。

我正在使用MVVM模式,所以我的ViewModel几乎不知道向用户展示模型的视图(UI)。

说我有我的ViewModel中的以下方法:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) { this.Messages.Add(e.Message); OnPropertyChanged("Messages"); } 

在我看来,我有一个ListBox绑定到ViewModel的Messages属性( List<string> )。 OnPropertyChanged通过调用PropertyChangedEventHandler来实现INotifyPropertyChanged接口的angular色。

我需要确保在UI线程上调用OnPropertyChanged – 我该怎么做? 我已经尝试了以下内容:

 public Dispatcher Dispatcher { get; set; } public MyViewModel() { this.Dispatcher = Dispatcher.CurrentDispatcher; } 

然后将以下内容添加到OnPropertyChanged方法:

 if (this.Dispatcher != Dispatcher.CurrentDispatcher) { this.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate { OnPropertyChanged(propertyName); })); return; } 

但是这不起作用。 有任何想法吗?

WPF自动将属性更改编组到UI线程。 但是,它不会收集更改编号,所以我怀疑您添加一条消息导致失败。

您可以手动编组添加手动(请参阅下面的示例),或者使用类似于我以前写博客的技术 。

手动编组:

 public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) { Dispatcher.Invoke(new Action<string>(AddMessage), e.Message); OnPropertyChanged("Messages"); } private void AddMessage(string message) { Dispatcher.VerifyAccess(); Messages.Add(message); } 

我真的很喜欢杰里米的回答: 在Silverlight中调度

概要:

  • 在ViewModel中放置Dispatcher似乎不够高雅

  • 创build一个Action <Action>属性,将其设置为仅在VM构造函数中运行该操作

  • 从V中使用VM时,请设置Action属性来调用Dispatcher

我本周也有类似的情况(这里也是MVVM)。 我有一个单独的类来完成它的事情,报告事件处理程序的状态。 事件处理程序正如预期的那样被调用,并且我可以看到使用Debug.WriteLine正确返回的结果

但是对于WPF,不pipe我做了什么,UI都不会更新,直到过程完成。 一旦过程完成,用户界面将按预期更新。 就好像它正在获取PropertyChanged,但等待线程完成,然后再一次执行UI更新。

(很令我沮丧的是,Windows.Forms中的代码与DoEvents和.Refresh()一样起作用。

到目前为止,我已经通过在自己的线程上启动进程来解决这个问题:

 //hook up event handler myProcess.MyEvent += new EventHandler<MyEventArgs>(MyEventHandler); //start it on a thread ... ThreadStart threadStart = new ThreadStart(myProcess.Start); Thread thread = new Thread(threadStart); thread.Start(); 

然后在事件处理程序中:

 private void MyEventHandler(object sender, MyEventArgs e) { .... Application.Current.Dispatcher.Invoke( DispatcherPriority.Send, (DispatcherOperationCallback)(arg => { //do UI updating here ... }), null); 

我不推荐这个代码,因为我仍然试图理解WPF线程模型,Dispatcher如何工作,为什么在我的情况下,UI将不会更新,直到过程完成,即使事件处理程序按预期调用由devise?)。 但迄今为止,这对我来说已经奏效了。

我发现这两个链接有帮助:

http://www.nbdtech.com/blog/archive/2007/08/01/Passing-Wpf-Objects-Between-Threads-With-Source-Code.aspx

http://srtsolutions.com/blogs/mikewoelmer/archive/2009/04/17/dealing-with-unhandled-exceptions-in-wpf.aspx

我在我的视图模型之外处理BackgroundWorker.ReportProgress事件,并将实际的BackgroundWorker实例和ViewModel传递给定义asynchronous方法的类。

asynchronous方法然后调用bgWorker.ReportProgress并传递一个包装委托作为用户(作为对象)的类。 我写作的匿名方法。

在事件处理程序中,我将其从对象转换回包装types,然后调用其中的委托。

所有这一切意味着我可以直接从asynchronous运行的代码编写UI更改,但它只是围绕它进行包装。

这更详细地解释:

http://lukepuplett.blogspot.com/2009/05/updating-ui-from-asynchronous-ops.html

这是更多的接受答案的扩展,但我做了这个与我的事件hander …

 using System.Threading; private void Handler(object sender, RoutedEventArgs e) { if (Thread.CurrentThread == this.Dispatcher.Thread) { //do stuff to this } else { this.Dispatcher.Invoke( new Action<object, RoutedEventArgs>(Handler), sender, e); } }