Easy usage of INotifyPropertyChanged with Property-Attribute


Published: 09.06.2010

Hello everyone,

today I want to share an implementation I’m using in my personal prototyping-Framework for Silverlight. This is important to mention, because I have tested it only for „works“ but not including any productive parameters like memory-usage or processing-time.

The meaning of a prototyping-framework for me is to get thinks working as quick as possible, to show the outcome to my customer. There are some things I need very often, and so I’m using base-classes a lot.

One of them is ModelBase. Every time I need a Model kind of class, I let it inherit from ModelBase and even my ViewModelBase is inheriting from it.

This base-class is implementing INotifyPropertyChanged and offers me ways to deal with this kind of properties. A typical implementation of a property within a model would look like this:

 	[IsNotifyingProperty(NotifyAlsoFor = "FullName"]
        public string FirstName
        {
            get
            {
                return GetProperty(this);
            }
            set
            {
                SetProperty(this, value);
            }
        }

You recognize two things. First the Attribute called IsNotifyingProperty and the other one are the two access-methods just taking the current object and in case of SetProperty the value to set. You see, its very easy to copy-and-paste if needed, and no further changes are necessary when the name changes (Except the binding somewhere).

The Implementation of the attribute looks like this:

namespace BaumannSolutions.Common.Attributes
{
    public class IsNotifyingProperty : Attribute
    {
        public string NotifyAlsoFor { get; set; }
    }
}

You see, there is one property in it, we will get to this later. A property itself isn’t enough to do what is planned, so there is some code in the constructor of ModelBase that looks like this:

var props = this.GetType().GetProperties();

            foreach (PropertyInfo pi in props)
            {
                var attr = pi.GetCustomAttributes(true);
                foreach (object o in attr)
                {
                    if (o is IsNotifyingProperty && !NotificationProperties.ContainsKey(pi.Name))
                    {
                        Type proptype = pi.PropertyType.GetType();
                        NotificationProperties.Add(pi.Name, ModelBase.GetDefault(proptype));

                        if (((IsNotificationChanged)o).NotifyAlsoFor != null)
                        {
                            NotificationAlsoProperties.Add(pi.Name, ((IsNotificationChanged) o).NotifyAlsoFor);
                        }
                    }
                }
            }

and these class wide collections:

protected Dictionary NotificationProperties = new Dictionary();
protected Dictionary NotificationAlsoProperties = new Dictionary();

As you see in the code, reflection is used to check for the properties and there attributes, and if IsNotificationProperty is found, the Property will be added to the collection NotificationProperties and there will be stored a default-value via a helper method. I wont publish it here, it should be very obvious what the method is doing.

You have also the chance to give a property, for which there should also be a PropertyChanged event in case the current Property is changed. I hope I’ve chosen a good example by using FirstName and FullName. If the FirstName is changed, you may want to raise PropertyChanged also for the FullName, cause FirstName is a subset of it.

the last parts we need are the two methods GetProperty and SetProperty. They look like this:

 

  protected V GetProperty(DependencyObject parent)
        {
             var name = GetPreviousMethodName(MethodBase.GetCurrentMethod()).Replace("get_", "");
             if (NotificationProperties.ContainsKey(name))
             {
                 try
                 {
                     var ret = NotificationProperties[name];
                     if (ret != null)
                         return (V) ret;

                     return default(V);
                 }
                 catch (Exception)
                 {
                     return (V)GetDefault(typeof(V));
                 }
                 
             }
            return default(V);
        }


 protected void SetProperty(DependencyObject parent, object value)
        {
            var name = GetPreviousMethodName(MethodBase.GetCurrentMethod()).Replace("set_", "");
            if (NotificationProperties.ContainsKey(name))
            {
                NotificationProperties[name] = value;
                RaisePropertyChanged(name);
            }

            if (NotificationAlsoProperties.ContainsKey(name))
            {
                    RaisePropertyChanged(NotificationAlsoProperties[name]);
            }
        }


  private string GetPreviousMethodName(MethodBase currentMethod)
        {
            string methodName = String.Empty;

            try
            {
                StackTrace sTrace = new StackTrace();

                for (Int32 frameCount = 0; frameCount < sTrace.FrameCount; frameCount++)
                {
                    StackFrame sFrame = sTrace.GetFrame(frameCount);
                    MethodBase thisMethod = sFrame.GetMethod();

                    if (thisMethod == currentMethod)
                    {
                        if (frameCount + 1 <= sTrace.FrameCount)
                        {
                            StackFrame prevFrame = sTrace.GetFrame(frameCount + 1);
                            MethodBase prevMethod = prevFrame.GetMethod();
                            methodName = prevMethod.Name;
                        }
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
            }

            return methodName;
        }

So, you read carefully and have recognized a third method I haven't mentioned yet 🙂 Its just a helper-method to get the name if the calling Method. In our cases it will be set_Firstname and get_Firstname, the getter and setter of the property. That's why you just have to pass this and don't need to give the property name as a parameter. Just to make it even more simple to use and copy-and-paste the code.

The GetProperty generic method returns the typed property-value from the inner-collection and the SetProperty sets the value within this collection. It also raises PropertyChanged for the value and if defined the PropertyChanged for a related value.

Hope that gives you some inspiration to implement your own little framework to make live easier. I have a similar implementation for DependencyProperties and I will write about that soon.

Cya

Sascha