MVVM属性取决于对象的图

本文关键字:对象 取决于 属性 MVVM | 更新日期: 2023-09-27 18:16:52

我和WPF+MVVM一起工作。

我有一个包含Customer属性的VMCustomerObservableCollectionOrders。每个Order都有ItemsObservableCollection。每个Items都有一个Price

现在,我在VM上有以下属性:

public double TotalPrice
{
    return Customer.Orders.Sum(x => x.Items.Sum(y => y.Price));
}

问题是无论何时在这个对象图的任何点发生变化- UI应该被通知TotalPrice已经更改-但它没有…

例如,如果Customer将从A更改为B,或将添加订单,或将删除项目,或将更改项目的价格等。

有人有一个优雅的解决方案吗?

谢谢。

MVVM属性取决于对象的图

是否支持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和其中的项的订阅和退订可能很棘手。