I just read an article by Micheal Sync about implementing property changed notifications with Expression Tree, I liked what I saw but didn't appreciate the inheritance requirement (as always). So I quickly whipped up three alternate way using extension methods, have a look:

using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace Orkpad.Samples
{
    public static class NotifyPropertyChangedExtensions
    {

        public static void NotifyPropertyChanged<TValue>(this Object target, 
            Expression<Func<TValue>> propertySelector, Action<string> notifier)
        {
            if (notifier != null)
            {
                var memberExpression = propertySelector.Body as MemberExpression;
                if (memberExpression != null)
                {
                    notifier(memberExpression.Member.Name);
                }
            }

        }

        public static void Notify<TValue>(this Action<string> notifier, 
            Expression<Func<TValue>> propertySelector)
        {

            if (notifier != null)
            {
                var memberExpression = propertySelector.Body as MemberExpression;
                if (memberExpression != null)
                {
                    notifier(memberExpression.Member.Name);
                }
            }

        }

        public static void Raise<TValue>(this PropertyChangedEventHandler handler, Expression<Func<TValue>> propertySelector)
        {

            if (handler != null)
            {
                var memberExpression = propertySelector.Body as MemberExpression;
                if (memberExpression != null)
                {
                    var _sender = ((ConstantExpression)memberExpression.Expression).Value;
                    handler(_sender, new PropertyChangedEventArgs(memberExpression.Member.Name));
                }
            }

        }

    }

    // I've tested this in Silverlight it works fine, should work in WPF too.
    public class NameValuePair : INotifyPropertyChanged
    {

        string _name;
        string _value;

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                this.NotifyPropertyChanged(()=> this.Name, RaisePropertyChanged);
                // I don't like to new up, but this is also usable
                //new Action<string>(RaisePropertyChanged).Notify(() => this.Value);
            }
        }

        public string Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
                PropertyChanged.Raise(() => this.Value);
            }
        }


#region INotifyPropertyChanged Members

        void RaisePropertyChanged(string name)
        {
            if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        public event PropertyChangedEventHandler PropertyChanged;

#endregion

    }
}

Of the three ways I've show, I like the last one using the PropertyChangedEventHandler best - it's quite compact and tidy. In fact, we can use similar extension-method "pattern" to call/raise handlers in various other scenarios. The other two ways are also useful if you don't have direct access to the event, like in cases of inheritance or where you have to go through a wrapper. Now, I can't speak to the performance of either solution - though, if someone can show figures on performance especially relative to the "normal way" that would be super!

// Recap

// Way 1
this.NotifyPropertyChanged(() => this.Name, RaisePropertyChanged);
// Way 2
new Action<string>(RaisePropertyChanged).Notify(() => this.Name);
// Way 3
PropertyChanged.Raise(() => this.Name);
 

Comments

trackback
DotNetKicks.com
on 30-Mar-09 11:21 AM
Trackback from DotNetKicks.com

Strongly-Typed Property Changed Notifications with Extension Methods

trackback
DotNetShoutout
on 30-Mar-09 11:24 AM
Trackback from DotNetShoutout

Super Strongly-Typed Property Changed Notifications with Extension Methods

Alexey Zakharov
Alexey Zakharov Russia
on 02-Apr-09 8:20 PM
Hi,

You've doing great work with your nRoute!! This post is also cool!!

Way 3 is really cool! My small remark: you don't need to write <this> - PropertyChanged.Raise(() => Name);

Thanks,
Alexey Zakharov.

Rishi
Rishi
on 03-Apr-09 3:18 AM
@Alexey, thanks for your kind words. And to <this> or not to <this> is a matter of personal taste, but ya, without that it becomes even neater.

trackback
Bolik
on 09-Apr-09 5:08 AM
Trackback from Bolik

Interesting Finds: 2009-04-20

trackback
Bolik
on 09-Apr-09 10:11 AM
Trackback from Bolik

Interesting Finds: 2009-04-20

Mike Brown
Mike Brown United States
on 20-Apr-09 7:49 PM
I like it. I had done something similar except my implementation involved reflecting on the sender to get the Event...didn't even think to make an extension off PropertyChangedEventHandler!

Great job as always.

Rishi
Rishi
on 25-Apr-09 11:50 PM
Thankz Mike, I'm going to include this next drop of nRoute but before that I'll put out some numbers on the performance. And maybe look to caching to see if performance can be helped.

trackback
Ork Pad
on 03-May-09 5:44 PM
Trackback from Ork Pad

nRoute and M-V-VM: A Match Made in C#

Carlos Cubas
Carlos Cubas United States
on 08-Jun-09 8:36 PM
I seem to have hit a limitation on this approach.

It seems that we cannot call the Raise Extension Method, if the PropertyChangedEventHandler is defined in a base class.

Rishi
Rishi
on 12-Jun-09 7:43 PM
@Carlos, when using with a base class you can use either way 1 or way 2 (see above). The main requirement is that you have an inherited method (say called RaisePropertyChanged) that would raise the notificaiton when passed-in a property name. So something like this would do in your inherited class..

this.NotifyPropertyChanged(() => this.Name, RaisePropertyChanged);

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading