Declarative approach to manage your Property Dependencies in a MVVM architecture

One pretty common scenario in MVVM architectures is that your ViewModel has several interdependent Properties.

Let’s consider the following example:

	public class Constants : Bindable
	{
		private int _factor;
		public int Factor
		{
			get { return _factor; }
			set { _factor = value; NotifyPropertyChanged(() => Factor); }
		}
	}


	public class Calculator : Bindable
	{
		private readonly Constants _constants;

		public Calculator(Constants constants)
		{
			_constants = constants;
		}

		private int _a;
		public int A
		{
			get { return _a; }
			set
			{
				_a = value;
				NotifyPropertyChanged(() => A);
			}
		}

		private int _b;
		public int B
		{
			get { return _b; }
			set
			{
				_b = value;
				NotifyPropertyChanged(() => B);
			}
		}

		public int Sum
		{
			get
			{
				return (A + B) * _constants.Factor;
			}
		}

		public int Div
		{
			get
			{
				return (B == 0 ? Int32.MaxValue : A / B) * _constants.Factor;
			}
		}

		public int Mul
		{
			get
			{
				return (A * B) * _constants.Factor;
			}
		}

		public int Sub
		{
			get
			{
				return (A - B) * _constants.Factor;
			}
		}

	}

As you can tell, we have a simple Calculator which does basic maths on the two integer Properties A and B and shows the result in the View. Sum, Sub, Mul and Div are evaluated on the fly. No setter is necessary.

The task is now to automatically reevaluate the Properties Sum, Sub, Mul and Div whenever A or B change. One common solution is to change Sum, Sub, Mul and Div to Getter/Setter Properties and explicitly set their values when A or B change:

		private int _a;
		public int A
		{
			get { return _a; }
			set
			{
				_a = value;
				NotifyPropertyChanged(() => A);

				Sum = (A + B) * _constants.Factor;
				Sub = (A - B) * _constants.Factor;
				Mul = (A * B) * _constants.Factor;
				Div = (B == 0 ? Int32.MaxValue : A / B) * _constants.Factor; ;
			}
		}

		private int _b;
		public int B
		{
			get { return _b; }
			set
			{
				_b = value;
				NotifyPropertyChanged(() => B);

				Sum = (A + B) * _constants.Factor;
				Sub = (A - B) * _constants.Factor;
				Mul = (A * B) * _constants.Factor;
				Div = (B == 0 ? Int32.MaxValue : A / B) * _constants.Factor;
			}
		}

		private int _sum;
		public int Sum
		{
			get { return _sum; }
			set
			{
				_sum = value;
				NotifyPropertyChanged(() => Sum);
			}
		}

		//Implementation of Sub, Mul and Div are analogous

This approach, however, has several draw downs:

  1. A and B now have to know, which properties depend on them. Whenever the calculation of Sum, Sub, Mul or Div changes we would have to remember to go back to all dependent Property setters and make sure they force the reevaluation of the property. This is not the desired behavior. A ViewModel only notifies all its observers that a certain Property has changed. The subscribers should then themselves decide how they want to react. This is the so called event/observer-pattern, which should allow us to fire events and forget, without having to bother about the subscribers.
  2. The Property Dependency is present in different spots:
    • The calculation itself by explicitly accessing the Properties it is dependent on
    • The setter of every single Property, the calculations dependent on, by forcing the reevaluation of the calculation
  3. The calculation logic is in every property setter, which influences the calculation output (this could be avoided by extracting another method which does the calculation)

My first step to avoid this problem was to call a single method in the constructor of the ViewModel, which registers all Property Dependencies:

		//...
		public Calculator(Constants constants)
		{
			_constants = constants;

			RegisterPropertyDependencies();
		}

		private void RegisterPropertyDependencies()
		{
			Property(() => Sum)
				.DependsOn(() => A)
					.AndOn(() => B)
					.AndOn(_constants, k => k.Factor);

			Property(() => Sub)
				.DependsOn(() => A)
					.AndOn(() => B)
					.AndOn(_constants, k => k.Factor);

			Property(() => Mul)
				.DependsOn(() => A)
					.AndOn(() => B)
					.AndOn(_constants, k => k.Factor);

			Property(() => Div)
				.DependsOn(() => A)
					.AndOn(() => B)
					.AndOn(_constants, k => k.Factor);
		}
		//...

The PropertyDependency Framework that I’ve introduced here is part of the underlying ViewModel base class and offers a declarative syntax to register all Property Dependencies in your ViewModel. The syntax is implemented using fluent interfaces.
The implementation hooks up to the PropertyChanged Events of all objects that expose the properties, the Property in question is dependent on. When it notices, that a dependent Property has changed, it fires the PropertyChanged Event for the dependent Property.

Example:

Property(() => Sum)
	.DependsOn(() => A)
		.AndOn(() => B)
		.AndOn(_constants, k => k.Factor);

The ViewModel subscribes to the PropertyChanged event of the _constants instance and waits for the Property “Factor” to change. In case it notices a change of that property, it automatically fires the PropertyChanged event for the local “Sum” Property. The same applies to the properties “A” and “B” (in this case it would subscribe to the PropertyChanged event of the this instance).

This approach has several advantages over the previous one:
1. The properties A and B can fire and forget. They don’t have to know about all Properties that are dependent on them.
2. As a consequence of 1., whenever we change the logic of one of the calculated Properties, we only have to go to one specific spot – the Method RegisterPropertyDependencies – and modify the registered PropertyDependency.
3. The Properties Sum, Sub, Mul and Div can simply be Getter Properties that contain the calculation logic. No Setter, which fires the PropertyChanged notification, is required. The logic is where it should be: At the Property, that represents the calculation result.

Looking at this implementation I was still not completely satisfied; mainly, because the Property Dependency is still visible in two totally different spots:
1. The RegisterPropertyDependencies method explicitly registers, which Properties a certain Property depends on.
2. The Getter of the calculation properties implicitly describe the Dependency by actually using their values during the calculation.

So my goal was to come up with a cost-efficient solution that allows me to have the declarative and explicit PropertyDependency registration at the same spot, where my Property is.
First I tried to use C# Attributes. However, they are simply too limited. You cannot use my so beloved Expression Syntax to retrieve the Properties’ names. You also cannot refer to external objects (in our case, the _constants instance), which the calculation depends on.

Here is the final solution I came up with:

		//....
		private int _a;
		public int A
		{
			get { return _a; }
			set
			{
				_a = value;
				NotifyPropertyChanged( () => A );
			}
		}

		private int _b;
		public int B
		{
			get { return _b; }
			set
			{
				_b = value;
				NotifyPropertyChanged( () => B );
			}
		}

		public int Sum
		{
			get
			{
				Property(() => Sum)
					.Depends(p => p.On(() => A)
								.AndOn(() => B)
								.AndOn(_constants, k => k.Factor));
								
								

				return (A + B) * _constants.Factor;
			}
		}

		public int Div
		{
			get
			{
				Property(() => Div)
					.Depends(p => p.On(() => A)
								.AndOn(() => B)
								.AndOn(_constants, k => k.Factor));
				
				return (B == 0 ? Int32.MaxValue : A / B) * _constants.Factor;
			}
		}

		public int Mul
		{
			get
			{
				Property(() => Mul)
					.Depends(p => p.On(() => A)
								.AndOn(() => B)
								.AndOn(_constants, k => k.Factor));

				return (A * B) * _constants.Factor;
			}
		}

		public int Sub
		{
			get
			{
				Property(() => Sub)
					.Depends(p => p.On(() => A)
								.AndOn(() => B)
								.AndOn(_constants, k => k.Factor));

				return (A - B) * _constants.Factor;
			}
		}
		//....

As you can see, the Property Registration is in the very same spot as the calculation logic.
No switching back and forth between the logic and the explicit Dependency Registration is necessary anymore. When you change the property implementation, you see immediately that there exists a PropertyDependency Registration and you know, that you have to modify it as well.
Furthermore, the PropertyDependency is registered the first time you access the Property. That means, if you show that Property in the UI and bind to it, the registration will automatically be done for you on the fly. As a consequence, if you have an orphan Property that no one uses, no unnecessary registration would take place.

The observing reader might have noticed that the syntax changed a bit.
It changed from

			Property(() => Sum)
				.DependsOn(() => A)
					.AndOn(() => B)
					.AndOn(_constants, k => k.Factor);

to

				Property(() => Sum)
					.Depends(p => p.On(() => A)
								.AndOn(() => B)
								.AndOn(_constants, k => k.Factor));

The Depends method now expects a Action delegate.
I introduced this change, because the usage of Expressions in C# are rather costly. The former solution would always evaluate all four Expressions (() => Sum, () => A, () => B, () => Factor).
The final solution, in contrast, only evaluates the very first Expression that points to the dependent Property. In case it notices that the Dependency has already been registered, it does nothing. This causes the other three Expressions to be evaluated only the very first time the Getter is accessed.

Important notice:
You might think that this pattern is too costly to use. However, it is just as “cheap” as the usage of the NotifyPropertyChanged version, which uses Expressions to extract the Property’s names.
(Evaluating the Sum property 10.000 times took 57 milliseconds on my machine.)

I am not going to explain the PropertyDependency registration syntax implementation in detail as I’ve already talked about the concept. If you are curious or want to use it in your own project, just download the attached sample project. I’ll be happy to answer all arising questions 🙂

Download sample project

Leave a Reply

Your email address will not be published. Required fields are marked *