代码之家  ›  专栏  ›  技术社区  ›  Arsen Mkrtchyan

InotifyPropertiesChanged和Calculated属性

  •  7
  • Arsen Mkrtchyan  · 技术社区  · 16 年前

    假设我有一个简单的类顺序,它有一个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计算的属性中调用raisePropertiesChanged(“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.raisePropertiesChanged(“totalPrice”)。请提出更好的解决方案

    谢谢

    4 回复  |  直到 7 年前
        1
  •  4
  •   Mark Seemann    16 年前

    可以检查是否应该同时从任何其他可能更改值的成员处引发此事件,但只有在实际情况下才这样做 改变 价值。

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

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

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

    var oldPrice = this.TotalPrice;
    // mutate object here...
    this.CheckTotalPrice(oldPrice);
    
        2
  •  7
  •   Community Mohan Dere    9 年前

    另一个解决方案是罗伯特·罗斯尼在这个问题中提出的:

    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; } }
    

    我还没有实现该属性的细节。我最好现在就着手解决这个问题。

        3
  •  2
  •   Simon Randy Burden    14 年前

    如果你使用 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; }
        }
    }
    
        4
  •  2
  •   KolA    7 年前

    在影响totalprice计算的属性中调用raisePropertiesChanged(“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。项目价格和数量不知道依赖于它们的是什么,也不关心提升财产的变化,取决于总价格。依赖树可以根据需要具有任意多个级别。