“Type safe” PropretyChanged for all INotifyPropertyChanged through extensions – not

In C#, the you need to implement the interface INotifyPropertyChanged to support property change notification – something that’s used extensively, especially when you bind GUIs to properties. Unfortunately, the event excepts the name of a property, which means that the code typically looks like this;

        private string _myValue = "";
        public string MyValue
        {
            get
            {
                return _myValue;
            }
            set
            {
                if (_myValue!=value)
                {
                    _myValue = value;
                    SendPropertyChanged("MyValue");
                }
            }
        }

        private void SendPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

This fails horribly as soon as you refactor the code – because you can easily change the name of the property (MyValue) but the string will still be called “MyValue”. At this point, the property changed event will be fired – but it will be reporting the wrong property name and thus the code is in fact broken.

This is easily solved by using lambda expressions instead;

Place this in a common ancestor;

        protected virtual void SendPropertyChanged<T>(Expression<Func<T>> action)
        {
            string propertyName = GetPropertyName(action);
            SendPropertyChanged(propertyName);
        }

        private void SendPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected static string GetPropertyName<T>(Expression<Func<T>> action)
        {
            var expression = (MemberExpression)action.Body;
            return expression.Member.Name;
        }

And in your subclasses fire the event using this code instead;

        private string _myValue = "";
        public string MyValue
        {
            get
            {
                return _myValue;
            }
            set
            {
                if (_myValue != value)
                {
                    _myValue = value;
                    SendPropertyChanged(() => MyValue);
                }
            }
        }

If you’re using this technique in a view model or in a business class, you’re golden!

But what if you want to use the code on any object that implements INotifyPropertyChanged?
I figured it would be trivial to create an extension class to add this functionality – should be straight forward enough!? Extensions can be applied to interfaces, so that should be no problem. Alas, it was not to be. My code looks like this – I’ve highlighted the parts that fail;

    public static class NotifyPropertyChangedExtensions
    {
        public static void SendPropertyChanged<T>(this INotifyPropertyChanged notifyPropertyChanged, Expression<Func<T>> action)
        {
            string propertyName = GetPropertyName(action);
            notifyPropertyChanged.SendPropertyChanged(propertyName);
        }

        public static void SendPropertyChanged(this INotifyPropertyChanged notifyPropertyChanged, string propertyName)
        {
            if (notifyPropertyChanged.PropertyChanged != null)
            {
                notifyPropertyChanged.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public static string GetPropertyName<T>(Expression<Func<T>> action)
        {
            var expression = (MemberExpression)action.Body;
            return expression.Member.Name;
        }
    }

The reason it fails is stated as;

The event ‘System.ComponentModel.INotifyPropertyChanged.PropertyChanged’ can only appear on the left hand side of += or -=

Apparently events can only accessed within the owning objects, not from the outside. Outsiders can only add or remove event handlers, never access the one’s that are there. Too bad.

About mfagerlund
Writes code in my sleep - and sometimes it even compiles!

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: