Improving MVVM INotifyPropertyChanged

 

With a project that uses MVVM (Model-View-View Model) design pattern, INotifyPropertyChanged is often used to detect a property value change in an object. This is particularly pertinent as far as creating a two-way data binding.

Typical Implementation

Normally, the INotifyPropertyChanged is implemented in a similar pattern to the code below. This is something that makes me feel a bit uneasy, especially in terms of maintainability and keeping things tidy, as every property needs to call the method ‘OnPropertyChanged’. All it takes is a typo, a missed call, and it could be hard to spot in a complex application.

The code below is acceptable, but could be simplified through abstraction.


using System.ComponentModel;

public class MyClass : INotifyPropertyChanged
{
	private int id { get; set; }
	public int Id
	{
		get { return id; }
		set 
		{
			id = value;
			OnPropertyChanged(“Id”);
		}
	}


	private string name { get; set; }
	public string Name
	{
		get { return name; }
		set
		{
			name = value;
			OnPropertyChanged(“Name”);
		}
	}

	public event PropertyChangedEventHandler PropertyChanged; 

	private void OnPropertyChanged(string propertyName)
	{
		PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}
}

Improved using Base Class

Since .NET 4.5, it is possible to harness an attribute called ‘CallerMemberName’. Visual Studio offers this as a base class template called ‘BindableBase’, which allows for a far neater implementation. I can’t take credit for the code below.


    /// 
    /// Implementation of  to simplify models.
    /// 
    [WebHostHidden]
    public abstract class BindableBase : INotifyPropertyChanged
    {
        /// 
        /// Multicast event for property change notifications.
        /// 
        public event PropertyChangedEventHandler PropertyChanged;

        /// 
        /// Checks if a property already matches a desired value.  Sets the property and
        /// notifies listeners only when necessary.
        /// 
        /// Type of the property.
        /// Reference to a property with both getter and setter.
        /// Desired value for the property.
        /// Name of the property used to notify listeners.  This
        /// value is optional and can be provided automatically when invoked from compilers that
        /// support CallerMemberName.
        /// True if the value was changed, false if the existing value matched the
        /// desired value.
        protected bool SetProperty(ref T storage, T value, [CallerMemberName] String propertyName = null)
        {
            if (Equals(storage, value)) return false;

            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }

        /// 
        /// Notifies listeners that a property value has changed.
        /// 
        /// Name of the property used to notify listeners.  This
        /// value is optional and can be provided automatically when invoked from compilers
        /// that support .
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Then any subsequent classes just need to inherit BindableBase.


public class MyViewModel : BindableBase
{
	private string _someStringProperty;
	
	public string SomeStringProperty
	{
		get => _someStringProperty;
		set => SetProperty(ref _someStringProperty, value);
	}

}

 

Post Categories