我强烈推荐 MVVM,但是当涉及到这样的分层模型时,如果您愿意并且能够对模型类本身进行一些简单的修改,则可以省去很多麻烦。
当您编写“无效”其他计算属性的属性和集合时,它会很快变得复杂。在我的应用程序中,我使用了一种基于属性的方法,该方法允许我使用 DependsOn 属性来装饰属性,以便每当更改依赖属性时,也会为计算属性引发属性更改事件。在你的情况下,我认为你不需要那样全力以赴。
但我认为真正派上用场的一件事是派生自ObservableCollection 的类,由于目前没有更好的术语,我将其称为ObservableCollectionEx。它添加的一件事是一个ItemPropertyChanged 事件,它允许您将一个处理程序连接到它,并且只要集合内的项目的属性发生更改,它就会被引发。这要求您在添加或删除项目时连接到INotifyPropertyChanged。但是一旦这个类不碍事,级联的属性更改就变得轻而易举了。
我建议您创建一个类似于以下内容的 CarListViewModel(或您想调用的任何名称)。请注意,最终结果是对象的层次结构,其中对读/写 Part.Total 属性的任何修改都会导致 PropertyChanged 事件的连锁反应,这些事件会冒泡到 ViewModel。下面的代码并不完全完整。您需要提供 INotifyPropertyChanged 的实现。但最后你会看到我提到的 ObservableCollectionEx。
编辑:我刚刚单击了您原始帖子中的链接,并意识到您已经实现了 ObservableCollectionEx。我选择使用 InsertItem、RemoveItem 等方法而不是 OnCollectionChanged 来挂钩/取消挂钩项目事件,因为在其他实现中存在一个令人讨厌的问题 - 如果清除集合,则不再有要取消挂钩的项目集合。
class CarListViewModel : INotifyPropertyChanged {
public CarListViewModel() {
Cars = new ObservableCollectionEx<Car>();
Cars.CollectionChanged += (sender,e) => OnPropertyChanged("Total");
Cars.ItemPropertyChanged += (sender,e) => {
if (e.PropertyName == "Total") {
OnPropertyChanged("Total");
}
}
}
public ObservableCollectionEx<Car> Cars {
get;
private set;
}
public decimal Total {
get {
return Cars.Sum(x=>x.Total);
}
}
}
class Car : INotifyPropertyChanged {
public Car() {
Parts = new ObservableCollectionEx<Part>();
Parts.CollectionChanged += (sender,e) => OnPropertyChanged("Total");
Parts.ItemPropertyChanged += (sender,e) => {
if (e.PropertyName == "Total") {
OnPropertyChanged("Total");
}
}
}
public ObservableCollectionEx<Part> Parts {
get;
private set;
}
public decimal Total {
get {
return Parts.Sum(x=>x.Total);
}
}
}
class Part : INotifyPropertyChanged {
private decimal _Total;
public decimal Total {
get { return _Total; }
set {
_Total = value;
OnPropertyChanged("Total");
}
}
}
class ObservableCollectionEx<T> : ObservableCollection<T>
where T: INotifyPropertyChanged
{
protected override void InsertItem(int index, T item) {
base.InsertItem(index, item);
item.PropertyChanged += Item_PropertyChanged;
}
protected override void RemoveItem(int index) {
Items[index].PropertyChanged -= Item_PropertyChanged;
base.RemoveItem(index);
}
protected override void ClearItems() {
foreach (T item in Items) {
item.PropertyChanged -= Item_PropertyChanged;
}
base.ClearItems();
}
protected override void SetItem(int index, T item) {
T oldItem = Items[index];
T newItem = item;
oldItem.PropertyChanged -= Item_PropertyChanged;
newItem.PropertyChanged += Item_PropertyChanged;
base.SetItem( index, item );
}
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) {
var handler = ItemPropertyChanged;
if (handler != null) { handler(sender, e); }
}
public event PropertyChangedEventHandler ItemPropertyChanged;
}