MVVM update of calculated properties MVVM update of calculated properties wpf wpf

MVVM update of calculated properties


The solution most often offered here seems to be to get all of the properties on which the tax value depends to invoke PropertyChanged in the ModelView for the property itself and for every property that depends on it.

No the supporting properties do not need their own change notification unless they are being displayed. But each property will need to call the tax value's OnPropertyChanged("TaxValue") in their setter(s) either directly; or indirectly as per the example below. That way the UI gets updated because a supporting property has changed.

With that said, let us consider an example. One way is to create a method which will do the value calculation. When the ultimate value is set (TaxValue below) it will call OnNotifyPropertyChange. That operation will inform the user of the TaxValue change to the whole world; regardless of what value triggers it (Deduction|Rate|Income):

public class MainpageVM : INotifyPropertyChanged {       public decimal TaxValue         {           get { return _taxValue; }           set { _taxValue = value; OnPropertyChanged(); }  // Using .Net 4.5 caller member method.        }        public decimal Deduction        {           get { return _deduction; }           set { _deduction = value; FigureTax(); }        }        public decimal Rate        {           get { return _rate; }           set { _rate = value; FigureTax(); }        }        public decimal Income        {           get { return _income; }           set { _income = value; FigureTax(); }        }        // Something has changed figure the tax and update the user.        private void FigureTax()        {           TaxValue = (Income - Deduction) * Rate;        }    #region INotifyPropertyChanged        public event PropertyChangedEventHandler PropertyChanged;        /// <summary>Raises the PropertyChanged event.</summary>        /// <param name="propertyName">The name of the property that has changed, only needed        /// if called from a different source.</param>        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)        {            PropertyChangedEventHandler handler = PropertyChanged;            if (handler != null)            {                handler(this, new PropertyChangedEventArgs(propertyName));            }        }    #endif    }

Edit

To use CallerMemberName (and other items) in .Net 4 install the Nuget package:

Microsoft.BCL.

or if not use the standard OnPropetyChanged("TaxValue") instead.


Check out Stephen Cleary's Calculated Properties: https://github.com/StephenCleary/CalculatedProperties

It's very simple and does just this: propagates notifications of dependant properties without polluting the trigger property setter.

Primitive example:

public string Name {  get { return Property.Get(string.Empty); }  set { Property.Set(value); }} public string Greeting => Property.Calculated(() => "Hello, " + Name + "!");

It is incredibly powerful for its size: think Excel-like formula engine for View Model properties.

I used it in several projects both in domain and view model classes, it helped me to eliminate most of imperative control flow (a major source of errors) and make code much more declarative and clear.

The best thing about it is that dependent properties can belong to different view models and dependency graph can change dramatically during runtime and it still just works.


The solution most often offered here seems to be to get all of the properties on which the tax value depends to invoke PropertyChanged in the ModelView for the property itself and for every property that depends on it. ....

Yes, but only for that object: Each property should fire its own property change event in the setter. Additionally the setter should somehow trigger properties that depend on that value on itself. You should not try to trigger updates on other objects proactively: they should listen to this objects PropertyChanged.

The best solution I can think of is to make the class that does the calculation receive PropertyChanged events, and raise a new PropertyChanged event for the tax value when anything changes that goes into the calculation. That at least preserves encapsulation at class level, but it still breaches method encapsulation: the class shouldn't have to know about how a method does its work.

This is indeed the standard way. Each class has the responsibility to monitor the properties that it depends on, and fire property change events for the properties it.

There are probably frameworks that will help to do this for you, but it is worthwhile to know what should happen.