INotifyPropertyChanged 和计算属性

发布于 2024-08-21 02:14:43 字数 1137 浏览 7 评论 0原文

假设我有一个简单的类 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");在其他属性中。请提出更好的解决方案

非常感谢

Suppose I have simple class Order, that have a TotalPrice calculated property, which can be bound to 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; }    
  }
}

Is it a good practice to call RaisePropertyChanged("TotalPrice") in the properties that affect to TotalPrice calculation? What is the best way to refresh TotalPrice property?
The other version to do this of course is to change property like this

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");

    }
}

and call TotalPrice = -1 instead of this.RaisePropertyChanged("TotalPrice"); in other properties. please suggest solutions better

Thanks a lot

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

如果没有 2024-08-28 02:14:43

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

WPF INotifyPropertyChanged对于链接的只读属性

您可以创建一个属性依赖关系图(使用他的代码示例):

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

我还没有实现该属性的详细信息。我最好现在就开始做这件事。

Another solution is the one Robert Rossney proposed in this question:

WPF INotifyPropertyChanged for linked read-only properties

You can create a property dependency map (using his code samples):

private static Dictionary<string, string[]> _DependencyMap = 
new Dictionary<string, string[]>
{
   {"Foo", new[] { "Bar", "Baz" } },
};

and then do this in your OnPropertyChanged:

PropertyChanged(this, new PropertyChangedEventArgs(propertyName))
if (_DependencyMap.ContainsKey(propertyName))
{
   foreach (string p in _DependencyMap[propertyName])
   {
      PropertyChanged(this, new PropertyChangedEventArgs(p))
   }
}

You can even attach an attribute to tie the dependent property to the one it depends on. Something like:

[PropertyChangeDependsOn("Foo")]
public int Bar { get { return Foo * Foo; } }
[PropertyChangeDependsOn("Foo")]
public int Baz { get { return Foo * 2; } }

I haven't implemented the details of the attribute yet. I'd better get to working on that now.

哎呦我呸! 2024-08-28 02:14:43

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

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

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

然后您需要从其他变异成员中调用该方法:

var oldPrice = this.TotalPrice;
// mutate object here...
this.CheckTotalPrice(oldPrice);

It's fine to check to see if you should raise this event as well from any other member that may change the value, but only do so if you actually change the value.

You could encapsulate this in a method:

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

Then you need to call that from your other mutating members:

var oldPrice = this.TotalPrice;
// mutate object here...
this.CheckTotalPrice(oldPrice);
枕花眠 2024-08-28 02:14:43

在影响 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。依赖关系树可以根据需要具有任意多个级别。

Is it a good practice to call RaisePropertyChanged("TotalPrice") in the properties that affect to TotalPrice calculation?

No, it's not, it doesn't scale and (the fact that property should know everything that depends on it) is a maintenance nightmare

https://github.com/StephenCleary/CalculatedProperties is best formula engine as of now for MVVM (in my opinion) that notifies about changes of derived/calculated properties and supports any level of nesting, most importantly tree of dependencies can span across multiple objects and can dynamically change at runtime.

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

This is very similar to Excel formulas but for MVVM. ItemPrice nor Quantity don't know what depends on them and don't care about raising PropertyChanged for dependant TotalPrice. Tree of dependencies can have as many levels as needed.

陌上芳菲 2024-08-28 02:14:43

如果您使用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; }
    }
}

If you use NotifyPropertyWeaver you can have this code

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

And it will be compiled to this.

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; }
    }
}
吃→可爱长大的 2024-08-28 02:14:43

考虑使用 DependencyTracker,它是一个轻量级库,支持广泛的依赖项(您的示例,链,收藏品)。您甚至不必从任何特定的类派生您的类,或者用任何特殊对象包装您的依赖属性即可使其正常工作。

Consider using DependenciesTracker, it is a lightweight library that supports a wide range of dependencies (your example, chains, collection items). You even don't have to derive your class from any specific class or wrap your dependent property with any special object to get it worked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文