Jan 14 Keith | Dev

Strongly Typed INotifyPropertyChanged Event Raisers

INotifyPropertyChanged has been around forever, and so has the nasty string based property names that are passed to raise the events. Its all familiar stuff:

class Program 
{
    static void Main(string[] args)
    {
        
        var streamingPricesService = new StreamingPricesService();

        Expression<Action<Object, PropertyChangedEventArgs>> test = (o, e) => Console.WriteLine("Property '{0}' changed ", e.PropertyName);

        streamingPricesService.PropertyChanged 
            += (o, e) => Console.WriteLine("Property '{0}' changed ", e.PropertyName);
        streamingPricesService.PropertyChanged
            += (o, e) => Console.WriteLine("Property '{0}' changed ", e.PropertyName);
        streamingPricesService.IsOnline = true;

        Console.ReadKey();
    }

    private class StreamingPricesService : INotifyPropertyChanged
    {
        private bool _isOnline;
        public bool IsOnline
        {
            get { return _isOnline; }
            set
            {
                if (_isOnline != value)
                {
                    _isOnline = value;
                    InvokePropertyChanged("IsOnline");
                }
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

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

..and its nasty!!

Enter Lambda LambdaExpression, its been around the block by now,  you’ve all seen them, the PropertyChanged wireup below uses the meta data it exposes above, its type-safe and IMO cleaner. You can wrap the functionality up anyway you like, typically I’d put the INotifyPropertyChanged functionality in a class called NotifyingBase and keep the PropertyHelper in a separate class as its usually used in other situations too (however below its just all in 2 classes).

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

namespace ConsoleApplication3
{
    class Program 
    {
        static void Main(string[] args)
        {
            var streamingPricesService = new StreamingPricesService();

            streamingPricesService.PropertyChanged += (o, e) => Console.WriteLine("Property '{0}' changed ", e.PropertyName);
            streamingPricesService.PropertyChanged += (o, e) => Console.WriteLine("Property '{0}' changed ", e.PropertyName);
            streamingPricesService.IsOnline = true;

            Console.ReadKey();
        }

        private class StreamingPricesService : INotifyPropertyChanged
        {
            private bool _isOnline;
            public bool IsOnline
            {
                get { return _isOnline; }
                set
                {
                    if (_isOnline != value)
                    {
                        _isOnline = value;
                        InvokePropertyChanged(() => IsOnline);
                    }
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;

            private void InvokePropertyChanged<T>(Expression<Func<T>> expression)
            {
                PropertyChangedEventHandler changed = PropertyChanged;
                if (changed != null)
                {
                    var propertyName = PropertyHelper.GetPropertyName(expression);
                    changed(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }

    public class PropertyHelper
    {
        public static string GetPropertyName<T>(Expression<Func<T>> expression)
        {
            return GetPropertyNameFromLambda(expression);
        }

        public static string GetPropertyName<T>(Expression<Func<T, Object>> expression)
        {
            return GetPropertyNameFromLambda(expression);
        }

        private static string GetPropertyNameFromLambda(LambdaExpression lambda)
        {
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = lambda.Body as UnaryExpression;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }
            else
                memberExpression = lambda.Body as MemberExpression;

            if (memberExpression == null)
                throw new ArgumentException(String.Format("Property expression '{0}' did not provide a property name.", lambda));

            return memberExpression.Member.Name;
        }
    }
}

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

Keith Woods

Keith Woods works for Lab49, a consulting firm that builds advanced solutions for the financial services industry.