INotifyPropertyChanged和计算属性

假设我有一个简单的类Order,它有一个TotalPrice计算属性,可以绑定到WPF UI

public class Order : INotifyPropertyChanged { public decimal ItemPrice { get { return this.itemPrice; } set { this.itemPrice = value; this.RaisePropertyChanged("ItemPrice"); this.RaisePropertyChanged("TotalPrice"); } } public int Quantity { get { return this.quantity; } set { this.quantity= value; this.RaisePropertyChanged("Quantity"); this.RaisePropertyChanged("TotalPrice"); } } public decimal TotalPrice { get { return this.ItemPrice * this.Quantity; } } } 

在影响TotalPrice计算的属性中调用RaisePropertyChanged(“TotalPrice”)是一个好习惯吗? 刷新TotalPrice属性的最佳方法是什么? 当然这样做的另一个版本是改变这样的属性

 public decimal TotalPrice { get { return this.ItemPrice * this.Quantity; } protected set { if(value >= 0) throw ArgumentException("set method can be used for refresh purpose only"); } } 

并调用TotalPrice = -1而不是this.RaisePropertyChanged(“TotalPrice”); 在其他属性。 请更好地提出建议

非常感谢

可以检查是否应该从可能更改值的任何其他成员中提升此事件,但只有在实际更改值时才这样做。

您可以将其封装在一个方法中:

 private void CheckTotalPrice(decimal oldPrice) { if(this.TotalPrice != oldPrice) { this.RaisePropertyChanged("TotalPrice"); } } 

然后你需要从你的其他变异成员那里调用它:

 var oldPrice = this.TotalPrice; // mutate object here... this.CheckTotalPrice(oldPrice); 

另一个解决方案是Robert Rossney在这个问题中提出的解决方案:

WPF INotifyPropertyChanged用于链接的只读属性

您可以创建属性依赖关系图(使用他的代码示例):

 private static Dictionary _DependencyMap = new Dictionary { {"Foo", new[] { "Bar", "Baz" } }, }; 

然后在OnPropertyChanged中执行此操作:

 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)) if (_DependencyMap.ContainsKey(propertyName)) { foreach (string p in _DependencyMap[propertyName]) { PropertyChanged(this, new PropertyChangedEventArgs(p)) } } 

您甚至可以附加属性以将依赖属性绑定到它所依赖的属性。 就像是:

 [PropertyChangeDependsOn("Foo")] public int Bar { get { return Foo * Foo; } } [PropertyChangeDependsOn("Foo")] public int Baz { get { return Foo * 2; } } 

我还没有实现属性的细节。 我现在最好开始研究。

如果您使用NotifyPropertyWeaver,您可以拥有此代码

 public class Order : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public decimal ItemPrice { get; set; } public int Quantity { get; set; } public decimal TotalPrice { get { return ItemPrice*Quantity; } } } 

它将被编译为此。

 public class Order : INotifyPropertyChanged { decimal itemPrice; int quantity; public event PropertyChangedEventHandler PropertyChanged; public virtual void OnPropertyChanged(string propertyName) { var propertyChanged = PropertyChanged; if (propertyChanged != null) { propertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public decimal ItemPrice { get { return itemPrice; } set { if (itemPrice != value) { itemPrice = value; OnPropertyChanged("TotalPrice"); OnPropertyChanged("ItemPrice"); } } } public int Quantity { get { return quantity; } set { if (quantity != value) { quantity = value; OnPropertyChanged("TotalPrice"); OnPropertyChanged("Quantity"); } } } public decimal TotalPrice { get { return ItemPrice*Quantity; } } } 

在影响TotalPrice计算的属性中调用RaisePropertyChanged(“TotalPrice”)是一个好习惯吗?

不,它不是,它不规模,是一个维护噩梦。

https://github.com/StephenCleary/CalculatedProperties是迄今为止MVVM(在我看来)中最好的公式引擎,它通知了派生/计算属性的变化并支持任何级别的嵌套

  public decimal ItemPrice { get { return Property.Get(0m); } set { Property.Set(value); } } public int Quantity { get { return Property.Get(0); } set { Property.Set(value); } } public decimal TotalPrice { get { return Property.Calculated(() => ItemPrice * Quantity); } }