C# - INotifyPropertyChanged implementation with IsChanged events

By , 9/24/2012
(0 ratings)
Base class, which in addition to notifications through INotifyPropertyChanged additionally provides a IsChanged event for each property available. No string parameters are used, which avoids problems with refactoring.


Sample implementation INotifyPropertyChanged:

class Person : NotificationObjectWithPropertyIsChangedEvents
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name == value) return;
_Name = value;
RaisePropertyChanged(() => Name);
}
}

public Person(string name) {
Name = name;
}
}


Sample implementation IsChanged event:

var p = new Person("Max");
p.Property(() => p.Name).IsChanged += (s,e) => { Console.WriteLine("New name: " + (s as Person).Name); };
p.Name = "Smith";

Author: Janko, translation by Michael List
Translate to VB
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;

namespace eac.Utilities
{
    /// <summary>
    /// A helper class that provides events for Propery-IsChanged generically
    /// obj.Property(() => obj.PROPERTY).IsChanged += (s,e) => { ... };
    /// </summary>
    public class NotificationObjectWithPropertyIsChangedEvents : Object, INotifyPropertyChanged
    {
        public sealed class NotificationObjectWithPropertyIsChangedEventHolder
        {
            public event EventHandler IsChanged;

            readonly NotificationObjectWithPropertyIsChangedEvents _obj;
            public NotificationObjectWithPropertyIsChangedEventHolder(NotificationObjectWithPropertyIsChangedEvents obj)
            {
                _obj = obj;
            }

            public void RaiseIsChanged()
            {
                var tmp = IsChanged; // copy to local variable to be thread-safe
                if (tmp != null) tmp(_obj, EventArgs.Empty);
            }
        }

        public NotificationObjectWithPropertyIsChangedEvents()
        {
            this.PropertyChanged += NotificationObjectWithEvent_PropertyChanged;
        }

        readonly Dictionary<string, NotificationObjectWithPropertyIsChangedEventHolder> _eventHandler = new Dictionary<string, NotificationObjectWithPropertyIsChangedEventHolder>();

        public NotificationObjectWithPropertyIsChangedEventHolder Property<T>(Expression<Func<T>> propertyExpression)
        {
            var propName = ExtractPropertyName(propertyExpression);
            if (_eventHandler.ContainsKey(propName)) return _eventHandler[propName];

            var nh = new NotificationObjectWithPropertyIsChangedEventHolder(this);
            _eventHandler.Add(propName, nh);
            return nh;
        }

        void NotificationObjectWithEvent_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            foreach (var item in _eventHandler.Where(s => s.Key == e.PropertyName))
                item.Value.RaiseIsChanged();
        }

        
        // **************
        // Following code adopted from Prism 4.0 (http://compositewpf.codeplex.com/)

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if (propertyChanged != null)
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
        /// <summary>
        /// Raises this object's PropertyChanged event for each of the properties.
        /// </summary>
        /// <param name="propertyNames">The properties that have a new value.</param>
        protected void RaisePropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                throw new System.ArgumentNullException("propertyNames");
            string[] strArrays = propertyNames;
            foreach (var str in strArrays)
                this.RaisePropertyChanged(str);
        }
        protected void RaisePropertyChanged<T>(System.Linq.Expressions.Expression<System.Func<T>> propertyExpression)
        {
            string str = ExtractPropertyName<T>(propertyExpression);
            this.RaisePropertyChanged(str);
        }
        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>        
        public virtual event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        static string ExtractPropertyName<T>(System.Linq.Expressions.Expression<System.Func<T>> propertyExpression)
        {
            if (propertyExpression == null)
            {
                throw new System.ArgumentNullException("propertyExpression");
            }
            System.Linq.Expressions.MemberExpression body = propertyExpression.Body as System.Linq.Expressions.MemberExpression;
            if (body == null)
            {
                throw new System.ArgumentException("The expression is not a member access expression");
            }
            System.Reflection.PropertyInfo member = body.Member as System.Reflection.PropertyInfo;
            if (member == null)
            {
                throw new System.ArgumentException("The member access expression does not access a property");
            }
            System.Reflection.MethodInfo getMethod = member.GetGetMethod(true);
            if (getMethod.IsStatic)
            {
                throw new System.ArgumentException("The referenced property is a static property");
            }
            return body.Member.Name;
        }
    }
}

Comments

 

Log in, to comment!