Simplifying Working With INotifyPropertyChanged

I came across this post on Marlon Grech’s blog. Marlon has written a very simple wrapper for registering and unregistering INotifyPropertyChanged event handlers. I modified his solution slightly, doing caching the results of the PropertyInfo resolution instead of recalculating it every time in the PropertyChangedSubscriber.

Here is my version of the PropertyChangedSubscriber.

    /// <summary>
    /// Shortcut to subscribe to PropertyChanged on an INotfiyPropertyChanged and executes an action when that happens
    /// </summary>
    /// <typeparam name="TSource">Must implement INotifyPropertyChanged</typeparam>
    /// <typeparam name="TProperty">Can be any type</typeparam>
    public class PropertyChangedSubscriber<TSource, TProperty>
        : IDisposable where TSource : class, INotifyPropertyChanged
    {
        private readonly PropertyInfo _propertyInfo;
        private readonly TSource _source;
        private Action<TSource> _onChange;

        public PropertyChangedSubscriber(TSource source, Expression<Func<TSource, TProperty>> property)
        {
            var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
            if (propertyInfo == null)
            {
                throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
            }
            _propertyInfo = propertyInfo;

            _source = source;
            source.PropertyChanged += SourcePropertyChanged;
        }

        private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (IsTargetProperty(e.PropertyName))
            {
                _onChange(sender as TSource);
            }
        }

        /// <summary>
        /// Executes the action and returns an IDisposable so that you can unregister 
        /// </summary>
        /// <param name="onChanged">The action to execute</param>
        /// <returns>The IDisposable so that you can unregister</returns>
        public IDisposable Do(Action<TSource> onChanged)
        {
            _onChange = onChanged;
            return this;
        }

        /// <summary>
        /// Executes the action only once and automatically unregisters
        /// </summary>
        /// <param name="onChanged">The action to be executed</param>
        public void DoOnce(Action<TSource> onChanged)
        {
            Action<TSource> dispose = x => Dispose();
            _onChange = (Action<TSource>)Delegate.Combine(onChanged, dispose);
        }

        private bool IsTargetProperty(string propertyName)
        {
            return _propertyInfo.Name == propertyName;
        }

        #region Implementation of IDisposable

        /// <summary>
        ///   Unregisters the property
        /// </summary>
        public void Dispose()
        {
            _source.PropertyChanged -= SourcePropertyChanged;
        }

        #endregion
    }

Leave a Reply