我和WPF+MVVM
一起工作。
我有一个包含Customer
属性的VM
。Customer
的ObservableCollection
为Orders
。每个Order
都有Items
的ObservableCollection
。每个Items
都有一个Price
。
现在,我在VM
上有以下属性:
public double TotalPrice
{
return Customer.Orders.Sum(x => x.Items.Sum(y => y.Price));
}
问题是无论何时在这个对象图的任何点发生变化- UI应该被通知TotalPrice
已经更改-但它没有…
例如,如果Customer
将从A更改为B,或将添加订单,或将删除项目,或将更改项目的价格等。
有人有一个优雅的解决方案吗?
谢谢。
是否支持ViewModels中的INotifyPropertyChanged/INotifyCollectionChanged接口?你应该能够手动触发任何属性,例如在任何属性的setter中,你可以触发OnPropertyChanged("TotalPrice")
,这样TotalPrice
的UI绑定也会更新。
要处理依赖对象的变化,你可以提供事件或类似的东西,这样ViewModel将能够订阅和处理底层对象的变化,例如,你有一些服务,它是在改变从数据库重新加载订单,所以只要有新的变化,你会更新UI。在这种情况下,OrdersService
应该公开event OrdersUpdated
, ViewModel可以订阅这个事件,并为受影响的属性触发PropertyChanged
事件。
让我们考虑一些情况作为例子,例如订单价格已经改变。谁负责这些变化?这是由用户通过UI完成的吗?
你可以在这里找到我几天前写的一篇有趣的文章,它确切地讨论了这个问题(以及它的解决方案…)
你可以实现"accumulator"属性来存储对象集合中值的总和。监听这些值的变化,并适当更新累加器。
(顺便说一句,我忘了提一下,这是真正的情况下,价值是昂贵的计算。否则,Sll的答案绝对是正确的。
像这样,把无聊的东西去掉:
class BasketOfGoods : INotifyPropertyChanged
{
ObservableCollection<Good> contents = new ObservableCollection<Good>();
public decimal Total
{
get { /* getter code */ }
set { /*setter code */ }
}
public BasketOfGoods()
{
contents.CollectionChanged += contents_CollectionChanged;
}
void contents_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (var newGood in e.NewItems) ((Good)newGood).PropertyChanged += BasketOfGoods_PropertyChanged;
}
void BasketOfGoods_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Price") Total = contents.Select(x => x.Price).Sum();
}
}
class Good : INotifyPropertyChanged
{
public decimal Price
{
{
get { /* getter code */ }
set { /*setter code */ }
}
}
我认为我最新的WPF努力MadProps很好地处理了这种情况。看一下这个示例主-细节场景。只要有从正在编辑的项目到VM的路径(例如,VM.TheCustomer.SelectedOrder.SelectedItem
或简单的VM.SelectedItem
), PropChanged事件将传播到VM,您可以写入:
public readonly IProp<Customer> TheCustomer;
public readonly IProp<double> TotalPrice;
protected override void OnPropChanged(PropChangedEventArgs args)
{
if (args.Prop.IsAny(TheCustomer, Item.Schema.Price))
{
TotalPrice.Value = TheCustomer.Value.Orders
.Sum(order => order.Items.Sum(item => item.Price.Value));
}
}
关于添加和删除项目或订单,我只会在ICommand
代码中添加显式更新,如VM.UpdateTotalPrice();
。管理ObservableCollections和其中的项的订阅和退订可能很棘手。