Click here to Skip to main content
Click here to Skip to main content

AOP Implementation of INotifyPropertyChanged

By , 11 Apr 2009
 

Introduction

This article describes how to implement the INotifyPropertyChanged interface with AOP using Postsharp.

Background

On attempting to implement the INotifyPropertyChanged interface, you'll most likely become frustrated within a short time, just because you have to do the same steps for each class.

Possible solutions (from bad to good)

Copy & Paste

OK, copy & paste will work here, but this will produce very high redundancy.

Example

We will take a look at this example at every solution.

/// <summary>
/// A Person-Class
/// </summary>
public class Person : INotifyPropertyChanged
{
    private string firstName;
    private string lastName;

    /// <summary >
    /// Firstname of the Person
    /// </summary >
    public string FirstName
    {
        get { return firstName; }
        set
        {
            firstName = value;
            FirePropertyChanged("FirstName");
            FirePropertyChanged("Name");
        }
    }

    /// <summary >
    /// Lastname of the Person
    /// </summary >
    public string LastName
    {
        get { return lastName; }
        set
        {
            lastName = value; FirePropertyChanged("LastName");
            FirePropertyChanged("Name");
        }
    }

    /// <summary >
    /// Complete Name
    /// </summary >
    public string Name
    {
        get
        {
            return FirstName + " " + LastName;
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public void FirePropertyChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }

    #endregion
}

Here, you can see the standard way of implementing the INotifyPropertyChanged interface.

Remove redundancy

As you can see, the first solution causes a high redundancy. Now, we realize the similarity and implement an abstract class.

Example

Here, we implement the INotifyPropertyChanged members and our Help-function 'FirePropertyChanged' in an abstract class, and inherit our Person class from this base class 'NotifyingObject'.

public abstract class NotifyingObject : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public void FirePropertyChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged.Invoke(this, 
              new PropertyChangedEventArgs(propName));
        }
    }

    #endregion
}

/// <summary >
/// A Person-Class
/// </summary >
public class Person : NotifyingObject
{ /*...*/ }

Now, you might say "That's it!". Yes, of course. It's a nice way to implement the interface. But in C#, there is no multiple inherency.

public class xyz{}
public class Person : NotifyingObject, xyz {}

The code above will not be compiled!

The AOP way

In this step, we transform our base class 'NotifyingObject' into an Aspect. After this step, your code will look like this:

Example

/// <summary >
/// A Person-Class
/// </summary >
[Notifying]
public class Person : INotifyPropertyChanged
{
    private string firstName;
    private string lastName;

    /// <summary >
    /// Firstname of the Person
    /// </summary >
    public string FirstName
    {
        get { return firstName; }
        [NotifyingDependency(DependencyProperty = "Name")]
        set { firstName = value; }
    }

    /// <summary >
    /// Lastname of the Person
    /// </summary >
    public string LastName
    {
        get { return lastName; }
        [NotifyingDependency(DependencyProperty = "Name")]
        set { lastName = value; }
    }

    /// <summary >
    /// Complete Name
    /// </summary >
    public string Name
    {
        get { return FirstName + " " + LastName; }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

Now, let's take a closer look at a few lines:

[Notifying]
public class Person : INotifyPropertyChanged

This attaches the NotifyingAttribute to the set_ method from every property of the declaring class. The notification will be fired when this method is exited. The NotifyingAttribute can only be attached to classes which implement the INotifyPropertyChanged interface.

[Notifying]
public class Person

The code above will cause a Postsharp error which looks like this: PostSharp: WindowsFormsApplication_3._5.Person has to implement INotifyPropertyChanged.

[NotifyingDependency(DependencyProperty = "Name")]

The Name property depends on FirstName and LastName. The notification will be fired if one of these properties get changed. The property with the passed name has to exist, else you will see a Postshar error which looks like this: PostSharp: WindowsFormsApplication_3._5.Person has no Property 'ThirdName'.

Code listing

NotifyingAttribute

[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method)]
public class NotifyingAttribute : OnMethodBoundaryAspect
{
    private string[] propertyNames;
    private FieldInfo typeFieldInfo;

    /// <summary>
    /// Do as much work as possible at the Compiletime
    /// see: http://doc.postsharp.org/1.5/ClassRef/html/
    //             fbc0e32a-2617-c9ef-cb9e-45cdc171dde0.htm
    /// </summary>
    public override void CompileTimeInitialize(System.Reflection.MethodBase method)
    {
        // get the DependencyAttributes of the method
        Attribute[] atts = Attribute.GetCustomAttributes(method, 
                             typeof(NotifyingDependencyAttribute));

        int attCount = atts.Count();

        this.propertyNames = new string[attCount + 1];

        // first Fired = Property
        this.propertyNames[0] = GetPropertyNameFromMethodName(method.Name);

        // Add the dependency properties
        for (int i = 0; i < attCount; i++)
        {
            string depProp = (atts[i] as NotifyingDependencyAttribute).DependencyProperty;

            this.propertyNames[i + 1] = (atts[i] as 
                 NotifyingDependencyAttribute).DependencyProperty;
        }
    }

    /// <summary>
    /// Validating the Usage of NotifyingAttribute
    /// see: http://doc.postsharp.org/1.5/ClassRef/html/
    ///            aa77e517-2c98-ebec-c0e1-6bb20d013c33.htm
    /// </summary>
    public override bool CompileTimeValidate(System.Reflection.MethodBase method)
    {
        // check on implemented INotifyPropertyChanged
        if (method.DeclaringType.GetInterface(typeof(INotifyPropertyChanged).Name) != null)
        {
            // set_* are of interest
            // IsSpecialName checked to avoid attaching to something
            // like this: public void set_FooBar()
            // Note:
            // If somebody knows a Method, which set IsSpecialName
            // to true (without property setters)
            // -> leve a comment. thanks!
            if (method.Name.StartsWith("set_") && method.IsSpecialName)
            {
                // check if NotifyingIgnoreAttribute is set
                Attribute[] atts = Attribute.GetCustomAttributes(method, 
                                          typeof(NotifyingIgnoreAttribute));
                return atts.Count() == 0;
            }
        }
        else
        {
            // Create Postsharp Error
            PostSharp.Extensibility.Message error = 
              new PostSharp.Extensibility.Message(SeverityType.Error, 
              "AOP0001", method.DeclaringType.ToString() + 
              " has to implement INotifyPropertyChanged", "#");

            MessageSource.MessageSink.Write(error);
        }
        return false;
    }

    /// <summary>
    /// Callback which is called after executing setter-code
    /// see: http://doc.postsharp.org/1.5/ClassRef/html/
    ///            4d72f4ea-11c5-cbce-b56b-b671c98cffaf.htm
    /// </summary>
    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
        // get Fieldinfo of event PropertyChangedEventHandler PropertyChanged 
        this.typeFieldInfo = eventArgs.Instance.GetType()
                                .GetFields(BindingFlags.Instance | 
                                           BindingFlags.NonPublic | 
                                           BindingFlags.Public)
                                .Where(f => f.FieldType == 
                                            typeof(PropertyChangedEventHandler))
                                .FirstOrDefault();

        // fire the Notify on the invoking Instance
        FireNotify(eventArgs.Instance);
    }

    /// <summary>
    /// Fire the Notifies on the invoking Instance
    /// </summary>
    /// <param name="Target">invoking Instance</param>
    private void FireNotify(object Target)
    {
        // Fieldinfo of PropertyChanged found
        if (this.typeFieldInfo != null)
        {
            // get the value of PropertyChanged
            PropertyChangedEventHandler evHandler = 
               typeFieldInfo.GetValue(Target) as PropertyChangedEventHandler;

            if (evHandler != null)
            {
                foreach (string prop in propertyNames)
                {
                    // invoke the event
                    evHandler.Invoke(Target, new PropertyChangedEventArgs(prop));
                }
            }
        }
    }

    /// <summary>
    /// Gets the Property Name from a setter method name
    /// </summary>
    /// <param name="methodName">setter method name</param>
    /// <returns>Property Name</returns>
    private string GetPropertyNameFromMethodName(string methodName)
    {
        return methodName.Substring(4);
    }
}

NotifyingDependencyAttribute

/// <summary>
/// Aspect for defining notify dependencies
/// </summary>
[Serializable]
public class NotifyingDependencyAttribute : OnMethodBoundaryAspect
{
    public string DependencyProperty { get; set; }

    /// <summary>
    /// Validating the Usage of NotifyingDependencyAttribute
    /// see: http://doc.postsharp.org/1.5/ClassRef/html/
    ///           aa77e517-2c98-ebec-c0e1-6bb20d013c33.htm
    /// </summary>
    public override bool CompileTimeValidate(MethodBase method)
    {
        // Dependency Property must exists
        if (!PropertyHelper.PropertyExists(method.DeclaringType, DependencyProperty))
        {
            // Create Postsharp Error
            PostSharp.Extensibility.Message error = 
              new PostSharp.Extensibility.Message(SeverityType.Error, 
              "AOP0002", method.DeclaringType.ToString() + 
              " has no Property '" + DependencyProperty + 
              "'", "#");

            MessageSource.MessageSink.Write(error);
            return false;
        }
        return true;
    }
}

NotifyingDependencyAttribute

This class can be used to accept properties from the notifying object.

/// <summary>
/// Aspect to except Setters of Properties of Classes marked with [Notifying]
/// </summary>
[Serializable]
public class NotifyingIgnoreAttribute : Attribute { }

HelpClass

public class PropertyHelper
{
    public static bool PropertyExists(Type t, string propertyName)
    {
        return t.GetProperty(propertyName) != null;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

xpete111
university
Germany Germany
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralSame as yoursmemberxiebbs16 Jul '10 - 19:53 
http://shecht.wordpress.com/2009/12/12/inotifypropertychanged-with-unity-interception-aop/[^]
GeneralMy vote of 1memberMichael Ulmann7 May '10 - 21:38 
part of postsharp samples
Generalnotifying parent objects when a child object changesmembernambr911 Mar '10 - 20:28 
How can i notify parent object if a child object changes?
It would be much apreciated if some one could show me how to do this.
Lets say i have a chain User - address ... when address changes how do i notify user?
GeneralMy vote of 1membersurajfrommumbai12 Apr '09 - 18:18 
This is a example of postsharp
QuestionWhy replicate the PostSharp example?memberDmitri Nesteruk12 Apr '09 - 9:05 
An example of implementing INotifyPropertyChanged comes with PostSharp, and it covers both C# and Silverlight (in 1.5). I'm pretty sure it's well-know by people who use PostSharp, whether version 1.0 or 1.5. What's special about your solution?
GeneralRe: Why replicate the PostSharp example?memberDmitri Nesteruk12 Apr '09 - 9:52 
The 1.0SP1 release contains an example for C#. The 1.5 release contains a Silverlight example. Both can be downloaded from http://postsharp.org
GeneralRe: Why replicate the PostSharp example?memberDmitri Nesteruk12 Apr '09 - 21:00 
Regarding explicit notification on dependencies - it's an interesting idea (I also wrote an article on this), but I think you could simply use PostSharp to analyse properties at compile-time to determine which depends on which. In my article I do it just after compilation, but with PostSharp, you can be even more efficient. So, you don't really need that extra aspect Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 11 Apr 2009
Article Copyright 2009 by xpete111
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid