The last solution I came up with is to encapsulate the logic of sending events to a dedicated class.
The class has an open Handle method, which has the same signature as the PropertyChangedEventHandler delegate, which means that it can be subscribed to the PropertyChanged event of any class that implements the INotifyPropertyChanged interface.
The class accepts delegates, like the often used DelegateCommand used by most WPF implementations, which means it can be used without subclassing.
The class is as follows:
public class PropertyChangedHandler { private readonly Action<string> handler; private readonly Predicate<string> condition; private readonly IEnumerable<string> properties; public PropertyChangedHandler(Action<string> handler, Predicate<string> condition, IEnumerable<string> properties) { this.handler = handler; this.condition = condition; this.properties = properties; } public void Handle(object sender, PropertyChangedEventArgs e) { string property = e.PropertyName ?? string.Empty; if (this.Observes(property) && this.ShouldHandle(property)) { handler(property); } } private bool ShouldHandle(string property) { return condition == null ? true : condition(property); } private bool Observes(string property) { return string.IsNullOrEmpty(property) ? true : !properties.Any() ? true : properties.Contains(property); } }
You can then register the property handler handler as follows:
var eventHandler = new PropertyChangedHandler( handler: p => { }, condition: p => { }, properties: new string[] { "Foo", "Bar" } ); aViewModel.PropertyChanged += eventHandler.Handle;
PropertyChangedHandler takes care of checking the PropertyName PropertyChangedEventArgs and ensures that the handler is triggered by changes to the properties of the right.
Note that PropertyChangedHandler also accepts a predicate, so that a handler delegate can be conditionally dispatched. The class also allows you to specify multiple properties so that one handler can bind to multiple properties at a time.
This can be easily expanded using some extension methods for more convenient registration of the handler, which allows you to create an event handler and subscribe to the PropertyChanged event in one method call and specify properties using expressions instead of strings to achieve something, it looks like this:
aViewModel.OnPropertyChanged( handler: p => handlerMethod(), condition: p => handlerCondition, properties: aViewModel.GetProperties( p => p.Foo, p => p.Bar, p => p.Baz ) );
This basically means that when the Foo , Bar or Baz handlerMethod , if handlerCondition true.
OnPropertychanged method OnPropertychanged provided to cover various event logging requirements.
If, for example, you want to register a handler that is called for any changed property event and is always executed, you can simply do the following:
aViewModel.OnPropertyChanged(p => handlerMethod());
If, for example, you want to register a handler that always runs, but only for one specific property change, you can do the following:
aViewModel.OnPropertyChanged( handler: p => handlerMethod(), properties: aViewModel.GetProperties(p => p.Foo) );
I found this approach very useful when writing WPF MVVM applications. Imagine that you have a scenario where you want to invalidate a command when any of the three properties change. Using the usual method, you will need to do something like this:
void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case "Foo": case "Bar": case "Baz": FooBarBazCommand.Invalidate(); break; .... } }
If you change the name of any of the viewModel properties, you will need to remember the event handler update to select the correct properties.
Using the PropertyChangedHandler class specified above, you can get the same result with the following:
aViewModel.OnPropertyChanged( handler: p => FooBarBazCommand.Invalidate(), properties: aViewModel.GetProperties( p => p.Foo, p => p.Bar, p => p.Baz ) );
We now have security at compile time. If any of the viewModel properties is renamed, the program will not be able to compile.