Auto-implement INotifyPropertyChanged with Aspects

21Jan09

While integrating ValidationAspects with WPF I wrote a PostSharp aspect that would weave INotifyPropertyChanged implementation into a class. Manually implementing INotifyPropertyChanged on a class is a pain and it’s easy to miss a property when adding code to fire the Property Changed event. The use of aspects here is a really good idea – with the use of a single attribute your class will implement INotifyPropertyChanged and fire the Property Changed event for every property setter, leaving your class uncluttered. Nice.

[NotifyPropertyChanged] // auto-implement INotifyPropertyChanged
public class Model
{
	public string Property1 { get; set; }
	public double Property2 { get; set; }
}
...
INotifyPropertyChanged notifyPropertyChanged = model as INotifyPropertyChanged;
notifyPropertyChanged.PropertyChanged += (s, e) => { /* do something */ };

However, I found that I didn’t actually need it in the end for the integration so it’ll be removed from the source. Not wanting to bin it entirely, I’ve pasted the source here on the chance that it might be of use to someone. Enjoy!

using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using PostSharp.Extensibility;
using PostSharp.Laos;

namespace MyAspects
{
	[Serializable]
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
	[MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false)]
	public class NotifyPropertyChangedAttribute : CompoundAspect
	{
		public int AspectPriority { get; set; }

		#region Overrides of CompoundAspect
		public override void ProvideAspects(object element, LaosReflectionAspectCollection collection)
		{
			Type type = (Type)element;

			collection.AddAspect(type, new PropertyChangedAspect {AspectPriority = AspectPriority});

			foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(pi => pi.GetGetMethod() != null && pi.GetSetMethod() != null))
			{
				collection.AddAspect(propertyInfo.GetSetMethod(), new NotifyPropertyChangedAspect(propertyInfo.Name) {AspectPriority = AspectPriority});
			}
		}
		#endregion
	}

	public interface IPropertyChanged : INotifyPropertyChanged
	{
		void NotifyChanged(string propertyName);
		void NotifyChanged(PropertyInfo property);
	}

	[Serializable]
	internal class PropertyChangedAspect : CompositionAspect
	{
		#region Overrides of CompositionAspect
		public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs)
		{
			return new PropertyChangedImpl(eventArgs.Instance);
		}

		public override Type GetPublicInterface(Type containerType)
		{
			return typeof(IPropertyChanged);
		}

		public override CompositionAspectOptions GetOptions()
		{
			return CompositionAspectOptions.GenerateImplementationAccessor;
		}
		#endregion
	}

	[Serializable]
	internal class NotifyPropertyChangedAspect : OnMethodBoundaryAspect
	{
		private readonly string _propertyName;

		public NotifyPropertyChangedAspect(string propertyName)
		{
			if (string.IsNullOrEmpty(propertyName))
				throw new ArgumentNullException("propertyName");

			_propertyName = propertyName;
		}

		public override void OnSuccess(MethodExecutionEventArgs eventArgs)
		{
			((PropertyChangedImpl)((IComposed<IPropertyChanged>)eventArgs.Instance).GetImplementation(eventArgs.InstanceCredentials)).NotifyChanged(_propertyName);
		}
	}

	[Serializable]
	internal class PropertyChangedImpl : IPropertyChanged
	{
		private readonly object _instance;

		public PropertyChangedImpl(object instance)
		{
			if (instance == null)
				throw new ArgumentNullException("instance");

			_instance = instance;
		}

		#region Implementation of INotifyPropertyChanged
		public event PropertyChangedEventHandler PropertyChanged;

		public void NotifyChanged(string propertyName)
		{
			if (string.IsNullOrEmpty(propertyName))
				throw new ArgumentNullException("propertyName");

			PropertyChangedEventHandler eventHandler = PropertyChanged;
			if (eventHandler != null)
				eventHandler(_instance, new PropertyChangedEventArgs(propertyName));
		}

		public void NotifyChanged(PropertyInfo property)
		{
			if (property == null)
				throw new ArgumentNullException("property");

			NotifyChanged(property.Name);
		}
		#endregion
	}
}
Advertisements


10 Responses to “Auto-implement INotifyPropertyChanged with Aspects”

  1. 1 yoram

    Hi,

    Some comments about your implementation:
    – You are adding the NotifyPropertyChangedAspect only to properties with a getter and a setter. Actually, you need only a set method, so I think you should raise a NotifyPropertyChanged event even on methods which don’t have a geter
    – In the NotifyPropertyChanged aspect, you are always raising the event, even if the value set does not change (e.g.: you put 4 in a property of type int, and 4 was already the value there). If the property has a getter, you can test for equality in the OnEntry aspect method, so you know if you should raise a NotifyPropertyChanged in the OnSuccess

    Nice work.

  2. 2 Mike

    @ yoram
    Thanks for your comment. These are good points and worth consideration for anyone building on this implementation for their own needs.

  3. Yep, and you don’t seem to check for static properties either. Anyways, this example is actually included in PostSharp, but guess what – even that example has errors, too 🙂

  4. 4 kovman

    Great job!!!

    Helped me a lot :-))) … thanks …

  5. I’m not that much of a online reader to be honest but your blogs really nice, keep it up!
    I’ll go ahead and bookmark your website to come back later on. Many thanks

  6. Hi, Neat post. There is a problem with your web site in internet explorer, would check this… IE still is the market leader and a good portion of people will
    miss your great writing due to this problem.

  7. I simply couldn’t depart your web site prior to suggesting that I extremely loved the usual info an individual provide to
    your guests? Is going to be again ceaselessly to check up on new
    posts


  1. 1 C and it’s sharp » INotifyPropertyChanged – Varianten für die Implementierung
  2. 2 INotifyPropertyChanged with Unity Interception AOP « Dmitry Shechtman's Blog
  3. 3 Implementing INotifyPropertyChanged - Erno de Weerd - blog community

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: