Adventures in MVVM — Dependant Properties with INotifyPropertyChanged

More Adventures in MVVM Shout it kick it on DotNetKicks.com

I spend a lot of time writing ViewModels, which almost always implements INotifyPropertyChanged.  For those who are not familiar with this interface, it includes a single event: PropertyChanged.  That event contains a payload of the name of the property that changed.  It exists as a standard way to notify observers that a property needs to be re-evaluated.

Although I use it all the time, I have always believed that INotifyPropertyChanged has some serious shortcomings.  One of those shortcomings deals with dependant properties.

Lets say, for for the sake of example, that the ViewModel has two integer properties that the user can enter (InputA and InputB).  There also exists a property named Calculation that is dependent upon InputA and InputB.  Finally, there is a display property named CalculationText which is dependent upon Calculation.  The code for these properties would look like this:

private int _inputA;
public int InputA
{
    get { return _inputA; }
    set
    {
        if (_inputA == value) return;

        _inputA = value;
        RaisePropertyChanged("InputA");
        RaisePropertyChanged("Calculation");
        RaisePropertyChanged("CalculationText");
    }
}

private int _inputB;
public int InputB
{
    get { return _inputB; }
    set
    {
        if (_inputB == value) return;

        _inputB = value;
        RaisePropertyChanged("InputB");
        RaisePropertyChanged("Calculation");
        RaisePropertyChanged("CalculationText");
    }
}

public int Calculation
{
    get { return InputA * InputB; }
}

public string CalculationText
{
    get { return "A * B = " + Calculation; }
}

There is a problem with this code.  The input properties need to know that they are inputs for Calculation and CalculationText by firing the event for the calculations.  As far as I am concerned, this is the wrong place for this information to exist.  Inputs should not know that they are inputs.  I can say first hand that this quickly breaks down as the ViewModel gets bigger and you start changing behavior.

The responsibility should be reversed.  Instead of having the input properties knowing about the dependants, the dependent properties should be responsible for knowing the inputs that they rely upon.  This can be done with some simple declarations:

private int _inputA;
public int InputA
{
    get { return _inputA; }
    set
    {
        if (_inputA == value) return;

        _inputA = value;
        RaisePropertyChanged("InputA");
    }
}

private int _inputB;
public int InputB
{
    get { return _inputB; }
    set
    {
        if (_inputB == value) return;

        _inputB = value;
        RaisePropertyChanged("InputB");
    }
}

[DependsUpon("InputA")]
[DependsUpon("InputB")]
public int Calculation
{
    get { return InputA * InputB; }
}

[DependsUpon("Calculation")]
public string CalculationText
{
    get { return "A * B = " + Calculation; }
}

Notice how Calculation declares that it is dependent upon InputA and InputBCalculationText also declares a dependency upon Calculation.  The inputs are free of knowing anything about the dependants.

The implementation of this behavior should be handled in the base class and there are several ways to accomplish it.  There are just a few things you want to think about:

  1. Make sure that when inputs fire a change notification, the dependants are also fired
  2. Make sure to handle dependency chaining.  InputA will cause Calculation to fire and Calculation will cause CalculationText to fire
  3. If the derived class declares dependencies such that it creates a circular reference, don’t endlessly loop or overflow your stack

Here is a very simple implementation of this behavior.  It manages the reflection at the time of change.  A better (more efficient) implementation would probably map the dependencies at construction time into a private dictionary, but this is just a quick example of how you might achieve this behavior:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    protected class DependsUponAttribute : Attribute
    {
        public string DependancyName { get; private set; }

        public DependsUponAttribute(string propertyName)
        {
            DependancyName = propertyName;
        }
    }

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        var handlers = PropertyChanged;
        if (handlers != null)
        {
            foreach(var property in AllNotifiedProperties(propertyName))
                handlers(this, new PropertyChangedEventArgs(property));
        }
    }

    private IEnumerable<string> DependantProperties(string inputName)
    {
        return from property in GetType().GetProperties()
               where property.GetCustomAttributes(typeof(DependsUponAttribute), true).Cast<DependsUponAttribute>()
                     .Any(attribute => attribute.DependancyName == inputName)
               select property.Name;
    }

    private IEnumerable<string> NotifiedProperties(IEnumerable<string> inputs)
    {
        var dependancies = from input in inputs
                           from dependancy in DependantProperties(input)
                           select dependancy;

        return inputs.Union(dependancies).Distinct();
    }

    private IEnumerable<string> AllNotifiedProperties(string inputName)
    {
        IEnumerable<string> results = new[]{inputName};

        while (NotifiedProperties(results).Count() > results.Count())
            results = NotifiedProperties(results);

        return results;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

6 Responses to “Adventures in MVVM — Dependant Properties with INotifyPropertyChanged”

  1. Brian, any way to take a similar approach with the properties in general. I am thinking something like:

    [Notify]
    public string FirstName { get; set; }

  2. admin says:

    Yes, you can do something like that, but it requires you to do some post compilation. See PostSharp for this: http://thetreeknowseverything.wordpress.com/2009/01/21/auto-implement-inotifypropertychanged-with-aspects/

  3. Vijay says:

    Brian,

    An excellent solution. I used a variation of this earlier, which required me to write lots of code. This is very elegant. Thanks a lot.

    Vijay

  4. Mike says:

    How would you handle properties raised by child models?

  5. bolu says:

    Brian, Is there any way to implement this on properties from another class?

    e.g.:
    [DependsUpon("InputA")]
    [DependsUpon("InputB")]
    [DependsUpon("ConvertRate",ClassA)]
    public int Calculation
    {
    get { return InputA * InputB * ClassA.ConvertRate; }
    }

  6. Mike says:

    I previously was making use of this neat class quite a bit. recently however, i have been finding some odd behaviors. I have list box contain a list of items. In code behind when when the list box item is clicked set a property of my viewmodel, which implements ViewModelBase. This causes the setter to RaisePropertyChanged. This all works fine.

    The problem is that for a model that is not flat..one that has child properties that are of some type that also implements ViewModelBase…for some reason there are cases where these child properties end up on other parent models. i haven’t been able to figure it out.

Leave a Reply