Damosse31
Damosse31

Reputation: 452

Calculated properties in Model

Bonjour !

I have Product model who contain many classics properties (Code, Description, Price With VAT, Price WITHOUT Vat, VAT). For my example I put only interesting properties.

In this model, I want to add prices calculations. When any amount change, to calculate other properties (When Vat change, recalculate Price With Vat and Price Without Vat, vice versa)

My example :

public class Product : EditableObject
{

    //VAT percentage propertie (ex: 20%)
    private decimal _vat;
    public decimal Vat
    {
        get { return _vat; }
        set
        {
            _vat = value;
            PriceWithVat = _priceWithoutVat * (1 + Vat / 100); //Vat changed, we recalculate Price WITH VAT propertie
            PriceWithoutVat = _priceWithVat / (1 + Vat / 100); //Vat changed, we recalculate Price WITHOUT VAT propertie
            NotifyOfPropertyChange(() => Vat);                
        }
    }

    //Product Price Without VAT
    private decimal _priceWithoutVat;
    public decimal PriceWithoutVat
    {
        get { return _priceWithoutVat; }
        set
        {
            _priceWithoutVat = value;                
            PriceWithVat = _priceWithoutVat * (1 + Vat / 100); //PriceWithoutVat changed, we recalculate Price WITH VAT propertie
            NotifyOfPropertyChange(() => PriceWithoutVat);
        }
    }

    //Product Price WITH Vat
    private decimal _priceWithVat;
    public decimal PriceWithVat
    {
        get { return _priceWithVat; }
        set
        {
            _priceWithVat = value;                
            PriceWithoutVat = _priceWithVat / (1 + Vat / 100); //PriceWithVat changed, we recalculate Price WITHOUT VAT propertie
            NotifyOfPropertyChange(() => PriceWithVat);
        }
    }       

 }

With this, when any price change, I have infinity loop and Stack Overflow. Normal, because when any price change, all others are recalculated, and they recalculate prices in turn :)

Do you have solution to automatically recalculate my 3 amounts when any of them change ?

Upvotes: 0

Views: 4095

Answers (1)

Dennis
Dennis

Reputation: 37790

Do not make calculated properties read-write. Instead, raise appropriate PropertyChanged for read-only calculated properties, e.g.:

public decimal Price
{
    get { return _price; }
    set
    {
        if (_price != value)
        {
            _price = value;
            NotifyOfPropertyChange(() => Price);
            NotifyOfPropertyChange(() => PriceWithVat);
        }
    }
}

public decimal Vat
{
    get { return _vat; }
    set
    {
        if (_vat != value)
        {
            _vat = value;
            NotifyOfPropertyChange(() => Vat);
            NotifyOfPropertyChange(() => PriceWithVat);
        }
    }
}

public decimal PriceWithVat
{
    get { return _price / (1 + _vat / 100); }
}

Since they are calculated properties, you can't set their values directly. On the other hand, raising PropertyChanged for them is enough to re-read their values from UI.

UPDATE

According to your comment (even though this is a very strange requirement), you can achieve what you want by updating backing field values. Note, that you must not call property setters here to avoid StackoverflowException:

private void UpdatePriceWithVat()
{
    _priceWithVat = _price * (1 + _vat / 100);
    NotifyOfPropertyChange(() => PriceWithVat);
}

private void UpdatePrice()
{
    _price = _priceWithVat / (1 + _vat / 100);
    NotifyOfPropertyChange(() => Price);
}

public decimal Price
{
    get { return _price; }
    set
    {
        if (_price != value)
        {
            _price = value;
            NotifyOfPropertyChange(() => Price);
            UpdatePriceWithVat();
        }
    }
}

public decimal Vat
{
    get { return _vat; }
    set
    {
        if (_vat != value)
        {
            _vat = value;
            NotifyOfPropertyChange(() => Vat);
            UpdatePriceWithVat();
        }
    }
}

public decimal PriceWithVat
{
    get { return _priceWithVat; }
    set
    {
        if (_priceWithVat != value)
        {
            _priceWithVat = value;
            NotifyOfPropertyChange(() => PriceWithVat);
            UpdatePrice();
        }
    }
}

Two notes:

  • term "calculated" isn't a best option here;
  • complexity of such code will grow very fast, if you'll add more properties, which will influence others.

Upvotes: 1

Related Questions