C#,WPF,MVVM和INotifyPropertyChanged

我感到困惑; 我以为我理解了INotifyPropertyChanged。

我有一个小的WPF应用程序,前端是MainWindow类,中间有一个视图模型,后面有一个模型。

在我的例子中,模型是Simulator类。

SimulatorViewModel几乎是透明的,只是MainWindow和Simulator之间的接口属性。

Simulator实现了INotifyPropertyChanged,Simulator中的每个属性setter都调用了RaisePropertyChanged方法:

private string serialNumber; public string SerialNumber { get { return serialNumber; } set { serialNumber = value; RaisePropertyChanged("SerialNumber"); } } public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propName)); } } 

在xaml中,我有一个带有这样绑定的TextBox:

 Text="{Binding Path=SerialNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 

DataContext是SimulatorViewModel(但请参阅我关于将DataContext更改为模型的注释)

ViewModel只传递属性:

 public string SerialNumber { get { return Simulator.SerialNumber; } set { Simulator.SerialNumber = value; } } 

Simulator中属性SerialNumber的编程更新不会传播到UI,但奇怪的是,在Simulator构造函数中设置的初始值正在到达那里。

如果我在模拟器中断开SerialNumber setter并进入RaisePropertyChanged,我发现PropertyChanged为null,因此事件不会向上传播到GUI。

这引出了几个问题:

  1. 究竟什么应该挂钩到PropertyChanged事件? 我希望比“WPF”更具体。 该事件与xaml中的Binding语句之间的联系是什么?

  2. 为什么初始属性值在启动时会到达UI,但后来却没有?

  3. 我是否有权使用模拟器(模型)实现INotifyPropertyChanged,或者应该是ViewModel这样做吗? 如果ViewModel执行此操作,则模型中的编程更改不会触发PropertyChanged,因此我不清楚正确的模式。 我意识到我的ViewModel实际上是多余的,但这是由于项目的简单性; 更复杂的一个会使ViewModel概念更加努力。 我的理解是ViewModel是我的unit testing接口的地方。

  1. 问题是您在模型上提升了PropertyChanged ,但您的视图绑定到ViewModel。 因此,您的View仅订阅ViewModel事件(而非Model) – 这就是文本框未更新的原因 – 因为它永远不会收到PropertyChanged事件。 一种可能的解决方案是在ViewModel中侦听Model PropertyChanged事件并相应地在ViewModel上引发相同的事件。

  2. 正在传播初始值,因为ViewModel中的setter / getter是正确的,问题出在事件中。

  3. 是的,你是对的(参见我的#1),你应该在ViewModel上引发PropertyChanged ,因为View绑定了它。 模型中的更改后,这些事件不会自动触发,因此您应该在ViewModel中侦听Model.PropertyChanged

最简单的脏修复来理解这个想法:

 SimulatorViewModel(Simulator model) { // this will re-raise Model's events on ViewModel // VM should implement INotifyPropertyChanged // method OnPropertyChanged should raise INPC for a given property model.PropertyChanged += (sender, args) => this.OnPropertyChanged(args.PropertyName); } 
  1. 始终确保已设置为Data-context的类的实例应实现INotifyPropertyChanged
  2. 在你的情况下,Simulator实现了INotifyPropertyChanged,但SimulatorViewModel被设置为DataContext,在这种情况下,只有在SimulatorViewModel中有更改但在模拟器中没有更改时,UI才会知道。

你能做的是:

您可以在Simulator类中公开某些事件或委托,并挂钩SimulatorViewModel类中的某些方法。 然后,当Simulator类中的值发生更改时,调用将在SimulatorViewModel上获取Execute的事件或委托,现在您可以从模拟器获取更新的值并更新SimulatorViewModel中的属性。 确保在类中使用不同的属性