Creating a strongly typed reactive wrapper to INotifyPropertyChanged

Practical Ugliness

INotifyPropertyChanged is a great, built-in, way for property change notification to work in the WPF/Silverlight world. Attempting to use it from staticly typed code, however, gets messy:

SomeViewModel viewModel;

viewModel.PropertyChanged += (s,e) =>
    {
        if (e.PropertyName == "TheProperty")
            GetToWork(viewModel.TheProperty);
    };

Things get even worse when we try to make this reactive:

var propertyChangedEvents = Observable.FromEvent(
    h => new PropertyChangedEventHandler(h),
    h => viewModel.PropertyChanged += h,
    h => viewModel.PropertyChanged -= h);

propertyChangedEvents
    .Where(x => x.PropertyName == "TheProperty")
    .Select(x => viewModel.TheProperty)
    .Subscribe(GetToWork);

An Expressive Solution

The extension method below allows you to specify the property you want to watch using an Expression<Func>, keeping things nice for the compiler:

Edit: Updated once it was tested (and simplified)

public static class NotifyPropertyChangeReactiveExtensions
{
    // Returns the values of property (an Expression) as they change,
    // starting with the current value
    public static IObservable<TValue> GetPropertyValues<TSource, TValue>(
        this TSource source, Expression<Func<TSource, TValue>> property)
        where TSource : INotifyPropertyChanged
    {
        MemberExpression memberExpression = property.Body as MemberExpression;

        if (memberExpression == null)
        {
            throw new ArgumentException(
                "property must directly access a property of the source");
        }

        string propertyName = memberExpression.Member.Name;

        Func<TSource, TValue> accessor = property.Compile();

        return source.GetPropertyChangedEvents()
            .Where(x => x.EventArgs.PropertyName == propertyName)
            .Select(x => accessor(source))
            .StartWith(accessor(source));
    }

    // This is a wrapper around FromEvent(PropertyChanged)
    public static IObservable<IEvent<PropertyChangedEventArgs>>
        GetPropertyChangedEvents(this INotifyPropertyChanged source)
    {
        return Observable.FromEvent<PropertyChangedEventHandler,
            PropertyChangedEventArgs>(
            h => new PropertyChangedEventHandler(h),
            h => source.PropertyChanged += h,
            h => source.PropertyChanged -= h);
    }
}

GetPropertyValues returns an IObservable of the values of the property as they change, starting with the current value.

You can then use it like so:

viewModel.GetPropertyChangeValues(x => x.TheProperty)
    .Subscribe(GetToWork);

I hope this method can be as useful to you as it has been to me.

About these ads

5 thoughts on “Creating a strongly typed reactive wrapper to INotifyPropertyChanged

  1. Pingback: endjin blog » Blog Archive » RX and INotifyPropertyChanged

  2. Pingback: endjin blog » Blog Archive » Layering your API

  3. Pingback: Layering your API | endjin blog

  4. Pingback: RX and INotifyPropertyChanged | endjin blog

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