BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

.NET Framework
.NET 4.5+

A Reusable INotifyPropertyChanged Extension Method

The INotifyPropertyChanged interface is applied to classes that need to create notifications when their property values are updated. One problem with the usual manner of implementing the interface is the use of literal strings to specify property names.

INotifyPropertyChanged Interface

INotifyPropertyChanged is a standard interface provided by the .NET framework. It is implemented by classes that must provide notifications of changes to their property values. This is a common approach for data classes that are the targets for binding, such as in Windows Presentation Foundation (WPF) applications.

The interface includes a single event, which is raised to indicate a change of object state. The event arguments for this event include the name of the property that was modified. This allows subscribers to the event to efficiently process the change, or ignore it if it applies to a property that is not relevant.

The name of the modified property is held in a string value in the event arguments. This means that it is easy to create defects. For example, you might misspell the property name when creating the event arguments. Another possibility is introducing a bug by performing refactoring. You might decide to rename a property using an automated refactoring tool and forget that the name in a literal string must be updated to match.

The code below shows a class that implements INotifyPropertyChanged and that includes a single property. The Name property's set accessor checks whether the new value matches the existing string. If it does not, the value in the backing store field is updated and the PropertyChanged event is raised. The if statement checks that there is at least one subscriber to the event before attempting to raise it.

You can see that the event arguments, in the PropertyChangedEventArgs type, include the property name as a literal.

public class Person : INotifyPropertyChanged
{
    string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                if (PropertyChanged != null)
                {
                    PropertyChangedEventArgs args = new PropertyChangedEventArgs("Name");
                    PropertyChanged(this, args);
                }
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Creating a Reusable, Strongly-Typed INotifyPropertyChanged Extension Method

It would be better if we could raise the PropertyChanged event without providing the property name as a literal string. If we could, there would be no chance that the name could be incorrectly spelled due to mistyping or the effect of an incomplete refactoring operation. In the remainder of this article we'll create an extension method that permits this, taking advantage of the Caller Information attributes that were introduced in the .NET framework version 4.5.

Let's start by creating a new, static class to hold the extension method:

public static class INotifyPropertyChangedExtensions
{
}

We'll be using the INotifyPropertyChanged interface, which is found in the System.ComponentModel namespace, and the caller information attributes from System.Runtime.CompilerServices. To make the code simpler, make sure you include the following using directives.

using System.ComponentModel;
using System.Runtime.CompilerServices;

We can now create the signature of our new Notify extension method. It requires three parameters. The first is the sender object that is raising the PropertyChanged event. This is the target of the extension method so is decorated with the "this" keyword.

The second parameter receives a PropertyChangedEventHandler object. Unfortunately, even though we will be supplying the object that raises the event, we will not be able to use its event from within our extension method. If we attempt to access it directly the code will not compile. However, we can pass in the handler from the sender and raise the event from it.

Finally, we need to create a string parameter that will contain the name of the changed property. We'll apply the CallerMemberName attribute to it so that we need never specify the name as a literal; the value will be set according to the member calling our extension method. This means that you must execute Notify from directly within the property. If you use it from another member that the property calls, the propertyName value will be incorrect and any bindings may fail to update.

public static void Notify(
    this INotifyPropertyChanged sender,
    PropertyChangedEventHandler handler,
    [CallerMemberName] string propertyName = "")
{
}

The content of the method is quite simple. We need to check if the provided handler has any subscribers by comparing it to null. If it isn't null, we can raise the event, passing event arguments that are constructed using the provided property name.

if (handler != null)
{
    PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
    handler(sender, args);
}

The entire static class containing the extension method is as follows:

public static class INotifyPropertyChangedExtensions
{
    public static void Notify(
        this INotifyPropertyChanged sender,
        PropertyChangedEventHandler handler,
        [CallerMemberName] string propertyName = "")
    {
        if (handler != null)
        {
            PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
            handler(sender, args);
        }
    }
}

Using the Extension Method

We can now update the Person class to use the extension method and to remove the literal string. The new version is shown below. As you can see, there is no longer a risk of providing an incorrect property name due to typing errors or refactoring.

public class Person : INotifyPropertyChanged
{
    string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                this.Notify(PropertyChanged);
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
15 January 2013