#1,205 – Use CallerMemberName Attribute to Make INotifyPropertyChanged Implementation Cleaner
April 24, 2017 3 Comments
The INotifyPropertyChanged interface is central to using data binding in WPF. You typically create ViewModels containing properties that fire a PropertyChanged event whenever a property value changes. Implementing this plumbing over and over for every property can become tedious. This argues for a reusable pattern to make the per-property code cleaner.
Specifying string-based property names when raising the PropertyChanged event can also be error-prone. When, passing property names as string literals, a misspelled property name doesn’t lead to a compiler warning, but just a quiet data binding failure.
Below is a pattern you can use to make property change code cleaner. It relies on the CallerMemberName attribute, which can be used to default a method parameter to the name of a caller–the property name in this case.
We have a generic method that compares a new property value to the current value in the backing variable. If the value has changed, it assigns the new value and fires the property changed event.
protected bool SetProp<T>(ref T backingField, T value, [CallerMemberName] string propName = null) { bool valueChanged = false; // Can't use equality operator on generic types if (!EqualityComparer<T>.Default.Equals(backingField, value)) { backingField = value; RaisePropertyChanged(propName); valueChanged = true; } return valueChanged; } public event PropertyChangedEventHandler PropertyChanged = delegate { }; protected void RaisePropertyChanged(string propName) { if (!string.IsNullOrWhiteSpace(propName) && (PropertyChanged != null)) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); } }
A standard property implementation can now look pretty clean. (Note: We’re not checking the return value of SetProp, but we could do that if we wanted to perform other logic when the property value changes).
private Dog _selectedDog; public Dog SelectedDog { get { return _selectedDog; } set { SetProp(ref _selectedDog, value); } }
You would generally put this sort of code in a common base class that all of your ViewModel classes could inherit from.