Jace
Jace

Reputation: 817

How to implement INotifyPropertyChanged for aggregate properties

Say I have a collection of order line objects...

public class OrderLine {
    public decimal Qty { get; set; }
    public decimal Cost { get; set; }
    public decimal CostExt { 
       get {
           return Qty * Cost;
       }
    }
}

I can implement INotifyPropertyChanged in Qty and Cost when those values are modified, and my view will be notified of the update. However, CostExt is an aggregate value, it relies on both Qty and Cost, meaning that I am forced to invoke PropertyChanged on it as well, in each property that it relates to! In a large view-model with potentially hundreds of properties and various aggregates sprinkled throughout, this would become a nightmare to maintain. Is there a way to coerce the view to re-examine the aggregate without any undue suffering?

Edit: OK, just to be clear, the design pattern I'm looking for will allow me to simply tell the aggregate(CostExt, in this case) what properties it should consider integral to its binding. Qty, Cost, and any other property involved should need to know nothing about the fact that they are being read from by the aggregate. The end result will be a much cleaner view-model without the need to rewrite existing properties because a new aggregate requires them. Simple, eh?

Upvotes: 2

Views: 444

Answers (3)

cubski
cubski

Reputation: 3258

If you don't want to manually implement INotifyPropertyChanged, use Fody PropertyChanged.

Your code:

[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

When compiled:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }

    string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set 
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged("FamilyName");
                OnPropertyChanged("FullName");
            }
        }
    }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Notice that properties that depends on other properties will get picked up.

Upvotes: 1

Mik3c
Mik3c

Reputation: 11

How about something like this:

public class OrderLine : INotifyPropertyChanged {

    decimal qty;
    decimal cost;

    public decimal Qty {
        get { return qty; }
        set { SetNotify(ref qty, value, "Qty", "CostExt"); }
    }

    public decimal Cost {
        get { return cost; }
        set { SetNotify(ref cost, value, "Cost", "CostExt"); }
    }

    public decimal CostExt {
        get {
            return Qty * Cost;
        }
    }

    protected void SetNotify<T>(ref T property, T value, params string[] notificationProperties) {
        var method = PropertyChanged;
        if (method != null) {
            foreach (var p in notificationProperties) {
                method(this, new PropertyChangedEventArgs(p));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Upvotes: 0

Hari Prasad
Hari Prasad

Reputation: 16966

Very simple, Raise ProperttyChanged event on CostExt when ever other dependent properties are changed.

   public decimal Qty
    {
        get { return _qty; }
        set
        {
            _qty = value; 
            OnPropertyChanged("Qty");
            OnPropertyChanged("CostExt");
        }
    }

Your (complete)implementation should be like

public class OrderLine : INotifyPropertyChanged
{
    private decimal _qty;
    private decimal _cost;

    public decimal Qty
    {
        get { return _qty; }
        set
        {
            _qty = value; 
            OnPropertyChanged("Qty");
            OnPropertyChanged("CostExt");
        }
    }

    public decimal Cost
    {
        get { return _cost; }
        set
        {
            _cost = value;
            OnPropertyChanged("Cost");
            OnPropertyChanged("CostExt");
        }
    }

    public decimal CostExt
    {
        get
        {
            return Qty * Cost;
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

Upvotes: 1

Related Questions