12,751,832 members (30,453 online)
alternative version

#### Stats

16.6K views
25 bookmarked
Posted 26 May 2014

# INotifyPropertyChanged Propagator

, 10 Jan 2017 CPOL
 Rate this:
Free and elegant way to propagate changes of dependent properties when dependency changed (including dynamically changeable nested properties)

## Attention

Instead of this code I strongly recommend to use Stephen's Cleary Nito.CalculatedProperties library: https://github.com/StephenCleary/CalculatedProperties.

Turned out Mr Cleary developed and published his library about the same time as I created my code so I wasn't aware of it. It is far more flexible and elegant than my solution, it's extremely simple and useful yet not so many people know about it.

## Introduction

Here is the free and elegant way to propagate changes of dependent properties when dependency changed (including dynamically changeable nested properties).

## Background

Most popular and naive implementation of `INotifyPropertyChanged `looks something like this:

```public decimal TotalPrice
{
get { return UnitPrice * Qty; }
}
public int Qty
{
get { return _qty; }
set
{
_qty = value;
RaisePropertyChanged(() => Qty);
RaisePropertyChanged(() => TotalPrice);
}
}
public decimal UnitPrice
{
get { return _unitPrice; }
set
{
_unitPrice = value;
RaisePropertyChanged(() => UnitPrice);
RaisePropertyChanged(() => TotalPrice);
}
}
```

It has one well known design flaw: independent properties know their dependencies (`Qty `and `UnitPrice `in this example know that `TotalPrice `depends on them).
It's not a big problem for small apps however in complex view model hierarchies, bookkeeping code grows fast and things get out of control pretty quickly.

Wouldn't it be cool to have something like Microsoft Excel sheet instead? You just type a formula depending on other cells while those cells have no idea what depends on them.

## Existing Solutions

This problem has been recognized by the community and number of possible solutions already exist. Why do we need another one? Well, let's check what we already have:

UpdateControls. Very promising and working solution. However it requires to abandon `INotifyPropertyChanged `along with good old `{Binding}` extensions and converters which is not easy in legacy code. Plus one should have some courage to give it a try on new commercial projects.

Fody (successor of Notify Property Weaver). Here we are stepping into the realm of IL-rewriters. Fody is free, fast and good. Unfortunately, it has a big limitation - namely it analyzes dependencies only in limits of one class, i.e., it won't propagate calculated property if it depends on a property of nested/child view model.

PostSharp. Also IL-rewriting solution, it has the same benefits that Fody plus it perfectly handles nested dependencies. Hooray! Unfortunately `INotifyPropertyChanged `implementation is a part of PostSharp Ultimate which is expensive (589 EUR as of 27 May 2014).

This list is not full obviously, there are other possible solutions with other or similar limitations. However, it looks like only PostSharp solves the problem to the full extent and unfortunately, it's not free.

## Introducing PropertyChangedPropagator

Long story short, it's better to explain how to use it by example (pay attention to statements in bold):

```public int Qty
{
get { return _qty; }
set { Set(ref _qty, value); }
}

public decimal UnitPrice
{
get { return _unitPrice; }
set { Set(ref _unitPrice, value); }
}

public decimal TotalPrice
{
get
{
RaiseMeWhen(this, has => has.Changed(_ => _.UnitPrice, _ => _.Qty));
return UnitPrice * Qty;
}
}

public decimal ExchTotalPrice
{
get
{
RaiseMeWhen(this, has => has.Changed(_ => _.TotalPrice, _ => _.ExchangeRate));
RaiseMeWhenDynamic(ExchangeRate, has => has.Changed(_ => _.Rate));
}
}

public ExchangeRateViewModel ExchangeRate
{
get { return _exchangeRate; }
set { Set(ref _exchangeRate, value); }
}
```

As you can see, the initial example has been turned inside out - `Qty `and `UnitPrice `properties (and `ExchangeRate.Rate `property of nested view model) don't know if there is anything depending on them, they just mind to fire their own notifications. And calculated properties say explicitly "yes, I'm depending on this and that, please raise me when some of those have changed".

How does `RaiseMeWhen `know which property to propagate? It uses `[CallerMemberName]` attribute:

```public void RaiseMeWhen<T>(T dep, Action<IRaiseMeWhenHas<T>> has,
[CallerMemberName] string propertyName = null) ...   ```

Though my solution does violate "don't repeat yourself" principle (as well as initial naive example), it encourages to declare dependencies just where they belong - in getters of dependent properties.

The trickiest part here is `RaiseMeWhenDynamic`. It should be used if some property depends on a property of nested view model and this nested view model itself is a subject to change at runtime. Sounds complicated, but in fact quite simple. In the attached example, we can choose one of `ExchangeRateViewModel `nested view models and change `Rate `property of currently active object.

A drawback of dynamic dependencies is that if there are several nested dependencies of the same type, then it's required to add some `string `hint to distinguish one dynamic dependency from another (fortunately this situation is rare). Consider the pseudo code:

```public string CurrentContactName
{
get
{
// PrimaryContact and SecondaryContact are of same type so have to use hints
RaiseMeWhenDynamic(PrimaryContact,
"Primary", has => has.Changed(_ => _.Name));
RaiseMeWhenDynamic(SecondaryContact,
"Secondary", has => has.Changed(_ => _.Name));
return (PrimaryContact ?? SecondaryContact).Name;
}
}
```

One of undeservedly little-known features of `INotifyPropertyChanged `is to fire `PropertyChanged `event with `string.Empty `property name which means "hey, the whole object changed, refresh all properties". `RaiseMeWhen `allows to do just that if invoked from constructor (NOTE: It doesn't work for dynamic dependencies). Another aggressive approach is to raise notification when any property of nested view model changed. The code below demonstrates both features:

```public MyViewModel(NestedViewModel nested)
{
NestedVm = nested;
RaiseMeWhen(NestedVm, has => has.ChangedAny());
} ```

Usage of `PropertyChangePropagator `might look like declarative code but there is no IL rewriting and the implementation is perfectly imperative. Which means that all propagation subscriptions are lazily created (or dynamically modified in case of dynamic subscriptions) only if getter of dependent property is invoked. In human words - if nobody reads the calculated property, then it won't be propagated. This "feature-like bug" is usually not a problem in real life (after all, you declare properties for views to read them), but makes unit testing a bit more tricky. :-) Check out the source code for details.

The implementation is also thread-safe and uses weak subscriptions, so it's safe to declare dependencies on long-living publishers as subscribers will be eligible for garbage collection when they go out of scope. There is also an implementation trick to optimize memory usage.

I highly recommend to download and run the source code to see more details.

### Performance

The solution includes performance tests which gives 1.5-2x times slower results than naive implementation. Analogue in PostSharp is 9 times slower on my machine, I didn't include it in the attached code though because of license dependency. In general, performance will depend on actual complexity of your view models' hierarchy.

Subscription callbacks created lazily on first getter invocation and then cached. In case of dynamic dependencies, subscriptions will be recreated on subsequent read after reference to the dynamic child view model changed.

Lambda expression trees to specify dependencies are themselves part of "`has`" callback's body and therefore constructed only once during subscription:

`RaiseMeWhen(this, has => has.Changed(_ => _.TotalPrice, _ => _.ExchangeRate));`

Propagation handlers made `static `to optimize memory footprint, i.e., same instances of generated methods are used for multiple view models of the same type. See StaticPropertyChangedHandlersCache class for implementation details.

## How to Add This to My Project?

Easy! Copy PropertyChangedPropagator.cs and WeakEventListener.cs and check BindableBaseEx.cs to get the idea how to include `RaiseMeWhen*` methods in your base view model class.

It works equally well in WPF and WinRT (didn't try Silverlight, but must be good too).

## Possible Improvements

It would be good to add support of `ObservableCollection<T>` and to find some way to work with multiple dynamic dependencies of the same type without `string `hints.

## Summary

`PropertyChangedPropagator `can be used as a free and easy solution to propagate property dependencies. It works nicely with legacy code and can be adopted gradually. However if you already have PostSharp Ultimate licence (or spare money for it), then I'd recommend to use it as it's a more mature solution with official support.

## History

• 5 June 2014; Added Performance section
• 27 May 2014: Initial version

## Share

 Software Developer New Zealand
Passionate .NET developer since times of C# 1.0
Originally from Belarus, moved to New Zealand in 2013

## You may also be interested in...

 First Prev Next
 GOOD JOB mikor-soft16-Feb-17 23:48 mikor-soft 16-Feb-17 23:48
 My vote of 5 Assil10-Jan-17 9:10 Assil 10-Jan-17 9:10
 My vote of 5 DVL Patel15-Feb-15 20:19 DVL Patel 15-Feb-15 20:19
 Similar projects ncdahlquist24-Nov-14 9:47 ncdahlquist 24-Nov-14 9:47
 Re: Similar projects Nick Dunets14-Nov-16 17:02 Nick Dunets 14-Nov-16 17:02
 Not bad. Pete O'Hanlon27-May-14 3:52 Pete O'Hanlon 27-May-14 3:52
 Re: Not bad. Nick Dunets27-May-14 11:01 Nick Dunets 27-May-14 11:01
 Re: Not bad. William E. Kempf30-May-14 10:37 William E. Kempf 30-May-14 10:37
 Re: Not bad. Nick Dunets4-Jun-14 13:16 Nick Dunets 4-Jun-14 13:16
 Re: Not bad. wkempf5-Jun-14 3:30 wkempf 5-Jun-14 3:30
 Re: Not bad. Nick Dunets5-Jun-14 11:37 Nick Dunets 5-Jun-14 11:37
 Nice, I quite like this Sacha Barber27-May-14 3:30 Sacha Barber 27-May-14 3:30
 Re: Nice, I quite like this Pete O'Hanlon27-May-14 8:47 Pete O'Hanlon 27-May-14 8:47
 Re: Nice, I quite like this Nick Dunets27-May-14 11:09 Nick Dunets 27-May-14 11:09
 Can you post the code ? melnac27-May-14 1:02 melnac 27-May-14 1:02
 Re: Can you post the code ? Nick Dunets27-May-14 10:59 Nick Dunets 27-May-14 10:59
 Last Visit: 31-Dec-99 19:00     Last Update: 21-Feb-17 11:01 Refresh 1