假设我有一个简单的类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 for linked read-only properties
您可以创建属性依赖关系图(使用他的代码示例):
private static Dictionary<string, string[]> _DependencyMap =
new Dictionary<string, string[]>
{
{"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); }
}
这与Excel公式非常相似,但对于MVVM。 ItemPrice或Quantity不知道取决于它们的内容,也不关心为依赖TotalPrice引发PropertyChanged。依赖树可以根据需要具有多个级别。