All posts tagged INotifyPropertyChanged

In this post I am going to introduce the idea of subscribing to property change events in our Model Base.

Note: Please read Part I before continuing here.
Note: Please read Part II before continuing here.

The purpose of this is to cover a potentially annoying situation:

Scenario: Let’s say you have a ViewModel, and inside that you have another ViewModel acting as a property like this:


    private MyCustomObject _customObjectInstance;

    public MyCustomObject CustomObjectInstance
    {
        get
        {
            return _customObjectInstance;
        }

        set
        {
            SetValue("CustomObjectInstance", ref _customObjectInstance, ref value);
        }
    }

Okay, now you want your UI bound to the current ViewModel to change every time CustomObjectInstance.MeterReading changes.

Q: (Panic moment) So, how do I do that without breaking my wonderful abstraction?
A: Implementing an ability to Subscribe to a nested property. All it takes is a little reflection and patience.

The idea is that we tell that property to fire a specific Action whenever it is changed in our ViewModelBase.

The first thing we will need to add is a private list of subscriptions:


/// <summary>
/// Subscription list.
/// </summary>
private readonly List<Tuple<string, Action<object>>> _subscriptions;

Instead of creating yet another custom object, I decided to use a Tuple because they are convenient.

  • The string value will serve as the name of the property you want to subscribe to.
  • The Action will serve as the Action you wish to call when the property is changed. The object parameter allows you to return an object if needed.

Next, we make sure to create a new instance of _subscriptions in the Constructor:


/// <summary>
/// Default constructor.
/// </summary>
protected ViewModelBase()
{
    _subscriptions = new List<Tuple<string, Action<object>>>();
}

Now we will expose a Subscribe method.

        /// <summary>
        /// Subscribes an action to a specific property that will be called
        /// during that property's OnPropertyChanged event.
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChange"></param>
        public void Subscribe(string propertyName, Action<object> onChange)
        {
            // Verify property
            var propInfo = this.GetType().GetProperty(propertyName);

            // If valid, add to subscription pool.
            if (propInfo != null)
            {
                _subscriptions.Add(
                    new Tuple<string, Action<object>>(propertyName, onChange));
            }
            else
            {
                // Invalid property name provided.
                throw new Exception(
                    "Property "" + propertyName + "" could not be " +
                    "found for type "" + this.GetType().ToString() + ""!");
            }
        }

This idea here is fairly simple:

  • We pass our property name and intended Action that will fire OnPropertyChanged.
  • If the property name is not valid, we will throw an exception to ensure we didn’t pass invalid information into our Subscribe method.

Q: Alright, now we have a nice Tuple-list full of property names and Actions. Now what?
A: Glad you asked. Here comes the hard part:


        /// <summary>
        /// Processes existing subscriptions matching the provided property name.
        /// </summary>
        /// <param name="propertyName"></param>
        private void ProcessSubscriptions(string propertyName)
        {
            // Get matching subscriptions
            var subList =
                (from p in _subscriptions
                 where p.Item1 == propertyName
                 select p).ToList();

            // Check if any matches were found.
            if (subList.Any())
            {
                // Process actions
                foreach (var sub in subList)
                {
                    // Evaluate action
                    var onChange = sub.Item2;
                    if (onChange != null)
                    {
                        // Get property value by name
                        var propInfo = this.GetType().GetProperty(propertyName);
                        var propValue = propInfo.GetValue(this, null);

                        // Invoke action
                        onChange(propValue);
                    }
                }
            }
        }

ProcessSubscriptions does the following:

  • Looks up a specific property by name in _subscriptions and gets a list of all entries that are registered for that property.
  • Loop: If a specific entry has a valid Action assigned to it, it will use reflection to get that property value and pass it to the action (as out object parameter mentioned earlier).

So, the last piece is making sure ProcessSubscriptions is fired when the property has been changed. And that is as easy as augmenting our trusted OnPropertyChanged method:


        /// <summary>
        /// Calls the PropertyChanged event
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChanged"></param>
        protected void OnPropertyChanged(string propertyName, Action onChanged = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                // Call handler
                handler(this, new PropertyChangedEventArgs(propertyName));

                // Subscriptions
                ProcessSubscriptions(propertyName);

                // On changed
                if (onChanged != null)
                {
                    onChanged();
                }
            }
        }

No worries if your specific property being changed is without entries. ProcessSubscriptions only acts on what is present in _subscriptions, so no entries means it just moves on.

Here is how you would use it in your parent ViewModel:


CustomObjectInstance.Subscribe("MeterReading", obj => MyActionThatDoesStuff());

Now, every time CustomObjectInstance.MeterReading is updated, the MyActionThatDoesStuff Action will be called allowing you to always have the latest values from your nested properties.

Here is our new ViewModelBase in it’s entirety:


    /// <summary>
    /// Extends the INotifyPropertyChanged interface to the class properties.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region Members

        /// <summary>
        /// Subscription list.
        /// </summary>
        private readonly List<Tuple<string, Action<object>>> _subscriptions;

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        protected ViewModelBase()
        {
            _subscriptions = new List<Tuple<string, Action<object>>>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// To be used within the "set" accessor in each property.
        /// This invokes the OnPropertyChanged method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="newValue"></param>
        /// <param name="onChanged"></param>
        protected void SetValue<T>(string name, ref T value, ref  T newValue,
            Action onChanged = null)
        {
            if (newValue != null)
            {
                if (!newValue.Equals(value))
                {
                    value = newValue;
                    OnPropertyChanged(name, onChanged);
                }
            }
            else
            {
                value = default(T);
            }
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// The PropertyChanged event handler.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Calls the PropertyChanged event
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChanged"></param>
        protected void OnPropertyChanged(string propertyName, Action onChanged = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                // Call handler
                handler(this, new PropertyChangedEventArgs(propertyName));

                // Subscriptions
                ProcessSubscriptions(propertyName);

                // On changed
                if (onChanged != null)
                {
                    onChanged();
                }
            }
        }

        /// <summary>
        /// Subscribes an action to a specific property that will be called
        /// during that property's OnPropertyChanged event.
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChange"></param>
        public void Subscribe(string propertyName, Action<object> onChange)
        {
            // Verify property
            var propInfo = this.GetType().GetProperty(propertyName);

            // If valid, add to subscription pool.
            if (propInfo != null)
            {
                _subscriptions.Add(
                    new Tuple<string, Action<object>>(propertyName, onChange));
            }
            else
            {
                // Invalid property name provided.
                throw new Exception(
                    "Property "" + propertyName + "" could not be " +
                    "found for type "" + this.GetType().ToString() + ""!");
            }
        }
        
        /// <summary>
        /// Clears the subscriptions.
        /// </summary>
        public void ClearSubscriptions()
        {
            _subscriptions.Clear();
        }

        /// <summary>
        /// Processes existing subscriptions matching the provided property name.
        /// </summary>
        /// <param name="propertyName"></param>
        private void ProcessSubscriptions(string propertyName)
        {
            // Get matching subscriptions
            var subList =
                (from p in _subscriptions
                 where p.Item1 == propertyName
                 select p).ToList();

            // Check if any matches were found.
            if (subList.Any())
            {
                // Process actions
                foreach (var sub in subList)
                {
                    // Evaluate action
                    var onChange = sub.Item2;
                    if (onChange != null)
                    {
                        // Get property value by name
                        var propInfo = this.GetType().GetProperty(propertyName);
                        var propValue = propInfo.GetValue(this, null);

                        // Invoke action
                        onChange(propValue);
                    }
                }
            }
        }

        #endregion
    }

I hope this has been helpful for you.

In this article, we are going to look at calling an Action when our property changes.

Note: Please read Part I before continuing here.

Alright, so let’s look at our ViewModelBase from Part I:


    /// <summary>
    /// Extends the INotifyPropertyChanged interface to the class properties.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region Methods

        /// <summary>
        /// To be used within the "set" accessor in each property.
        /// This invokes the OnPropertyChanged method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="newValue"></param>
        protected void SetValue<T>(string name, ref T value, ref  T newValue)
        {
            if (newValue != null)
            {
                if (!newValue.Equals(value))
                {
                    value = newValue;
                    OnPropertyChanged(name);
                }
            }
            else
            {
                value = default(T);
            }
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// The PropertyChanged event handler.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        
        /// <summary>
        /// Calls the PropertyChanged event
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }

Q: Okay, so what now?
A: Glad you asked. Let’s augment the above class:


    /// <summary>
    /// Extends the INotifyPropertyChanged interface to the class properties.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region Methods

        /// <summary>
        /// To be used within the "set" accessor in each property.
        /// This invokes the OnPropertyChanged method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="newValue"></param>
        protected void SetValue<T>(string name, ref T value, ref  T newValue, Action onChanged = null)
        {
            if (newValue != null)
            {
                if (!newValue.Equals(value))
                {
                    value = newValue;
                    OnPropertyChanged(name, onChanged);
                }
            }
            else
            {
                value = default(T);
            }
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// The PropertyChanged event handler.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        
        /// <summary>
        /// Calls the PropertyChanged event
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged(string propertyName, Action onChanged = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));

                // On changed
                if (onChanged != null)
                {
                    onChanged();
                }
            }
        }

        #endregion
    }

Essentially, we added an Action as a parameter to the SetValue method. This is then passed whenever the OnPropertyChanged method is called and executed after the event fires (if it is not null).

Using it is as easy as this:


    private string phoneNumberValue = String.Empty;

    public string PhoneNumber
    {
        get
        {
            return this.phoneNumberValue;
        }

        set
        {
            SetValue("PhoneNumber", ref this.phoneNumberValue, ref value, () => { UpdateSomeOtherUISection(); });
        }
    }

It’s not a common need, but sometimes something like this can really get you out of a bind when you need to update other areas of your application when this property changes.

Try it sometime.

Having used properties in an ObservableCollection, you are probably familiar with implementing INotifyPropertyChanged in your custom objects.

A property utilizing this implementation would look like this:

private string phoneNumberValue = String.Empty;

public string PhoneNumber
{
	get
	{
		return this.phoneNumberValue;
	}

	set
	{
		if (value != this.phoneNumberValue)
		{
			this.phoneNumberValue = value;
			NotifyPropertyChanged("PhoneNumber");
		}
	}
}

To remedy this repetitive implementation, I created this:

/// <summary>
/// Extends the INotifyPropertyChanged interface to the class properties.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
	#region Methods

	/// <summary>
	/// To be used within the "set" accessor in each property.
	/// This invokes the OnPropertyChanged method.
	/// </summary>
	/// <typeparam name="T"></param>
	/// <param name="name"></param>
	/// <param name="value"></param>
	/// <param name="newValue"></param>
	protected void SetValue&amp;amp;amp;amp;lt;T&amp;amp;amp;amp;gt;(string name, ref T value, ref  T newValue)
	{
		if (newValue != null)
		{
			if (!newValue.Equals(value))
			{
				value = newValue;
				OnPropertyChanged(name);
			}
		}
		else
		{
			value = default(T);
		}
	}

	#endregion

	#region INotifyPropertyChanged

	/// <summary>
	/// The PropertyChanged event handler.
	/// </summary>
	public event PropertyChangedEventHandler PropertyChanged;

	/// <summary>
	/// Calls the PropertyChanged event
	/// </summary>
	/// <param name="propertyName"></param>
	protected void OnPropertyChanged(string propertyName)
	{
		PropertyChangedEventHandler handler = PropertyChanged;
		if (handler != null)
		{
			handler(this, new PropertyChangedEventArgs(propertyName));
		}
	}

	#endregion
}

Then, all we need to do is inherit ViewModelBase in our custom object.

Now, our property would look something like this:

private string phoneNumberValue = String.Empty;

public string PhoneNumber
{
	get
	{
		return this.phoneNumberValue;
	}

	set
	{
		SetValue("PhoneNumber", ref this.phoneNumberValue, ref value);
	}
}

So, what was the reason for doing this?

  1. It avoids directly implementing INotifyPropertyChanged. This allows the ability to start up objects with this implementation much faster.
  2. Less coding and potential messes involving the set operator.

Update as of August 2022

Good news is since all the C# goodness over the last 10 years, I have greatly improved this implementation. It can be used like this:

private string _phoneNumberValue = String.Empty;

public string PhoneNumber
{
	get => _phoneNumberValue;
	set => NotifyOfChange(value, ref _phoneNumberValue);
}

Simple, right?