10 Stimmen

INotifyPropertyChanged und berechnete Eigenschaft

Angenommen, ich habe eine einfache Klasse namens Order, die eine berechnete Eigenschaft TotalPrice hat, die an die WPF-Benutzeroberfläche gebunden werden kann

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

Ist es eine gute Praxis, RaisePropertyChanged("TotalPrice") in den Eigenschaften aufzurufen, die sich auf die Berechnung von TotalPrice auswirken? Was ist der beste Weg, die TotalPrice-Eigenschaft zu aktualisieren? Die andere Version, dies zu tun, besteht natürlich darin, die Eigenschaft wie folgt zu ändern

public decimal TotalPrice
{
    get { return this.ItemPrice * this.Quantity; } 
    protected set 
    {
        if(value >= 0) 
            throw ArgumentException("Die set-Methode kann nur für Aktualisierungszwecke verwendet werden");

    }
}

und TotalPrice = -1 anstelle von this.RaisePropertyChanged("TotalPrice"); in anderen Eigenschaften aufzurufen. Bitte schlagen Sie bessere Lösungen vor

Vielen Dank

7voto

schmiddy98 Punkte 71

Eine weitere Lösung ist die, die Robert Rossney in dieser Frage vorgeschlagen hat:

WPF INotifyPropertyChanged für verknüpfte schreibgeschützte Eigenschaften

Sie können eine Eigenschaftsabhängigkeitskarte erstellen (mit seinen Codesamples):

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

und dann dies in Ihrem OnPropertyChanged tun:

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

Sie können sogar ein Attribut anhängen, um die abhängige Eigenschaft mit der abhängigen Eigenschaft zu verknüpfen. So etwas wie:

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

Ich habe die Details des Attributs noch nicht implementiert. Ich sollte jetzt besser daran arbeiten.

4voto

Mark Seemann Punkte 216836

Es ist in Ordnung zu überprüfen, ob Sie dieses Ereignis auch von einem anderen Mitglied aus lösen sollten, das den Wert ändern kann, aber tun Sie dies nur, wenn Sie den Wert tatsächlich ändern.

Sie könnten dies in einer Methode umschließen:

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

Dann müssen Sie das von Ihren anderen mutierenden Mitgliedern aufrufen:

var oldPrice = this.TotalPrice;
// Objekt hier verändern...
this.CheckTotalPrice(oldPrice);

4voto

KolA Punkte 657

Ist es eine gute Praxis, RaisePropertyChanged("TotalPrice") in den Eigenschaften aufzurufen, die die TotalPrice-Berechnung beeinflussen?

Nein, das ist es nicht, es skaliert nicht und (die Tatsache, dass die Eigenschaft alles wissen sollte, was von ihr abhängt) ist ein Albtraum in der Wartung

https://github.com/StephenCleary/CalculatedProperties ist meiner Meinung nach die beste Formel-Engine für MVVM, die über Änderungen von abgeleiteten/berechneten Eigenschaften informiert und jede Ebene der Verschachtelung unterstützt. Am wichtigsten ist, dass der Abhängigkeitsbaum über mehrere Objekte hinweg reichen kann und sich zur Laufzeit dynamisch ändern kann.

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

Dies ist sehr ähnlich zu Excel-Formeln, jedoch für MVVM. Weder ItemPrice noch Quantity wissen, wovon sie abhängen, und kümmern sich nicht darum, PropertyChanged für die abhängige TotalPrice auszulösen. Der Abhängigkeitsbaum kann so viele Ebenen haben, wie nötig sind.

2voto

Simon Punkte 32146

Wenn Sie NotifyPropertyWeaver verwenden, können Sie diesen Code haben

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

Und es wird zu diesem kompiliert.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X