Skip to content
Matthew Adams By Matthew Adams Co-Founder
RX and INotifyPropertyChanged

Richard Szalay has a great blog post which allows you to use the Reactive Extensions IObservable pattern to subscribe to INotifyPropertyChanged implementers.

However, the syntax is still a little bit clumsy.

By adding a few extra helpers to the class we can go from this:

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

To this

viewModel.Subscribe(x => x.SomeProperty, SomePropertyChanged);

Here's my slightly modified code.

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);
    }

    public static IDisposable
        Subscribe<TSource, TValue>(
            this TSource source,
            Expression<Func<TSource, TValue>> property,
            Action<TValue> observer)
        where TSource : INotifyPropertyChanged
    {
        return source
                .GetPropertyValues(property)
                .Subscribe(observer);
    }

    public static IDisposable
        Subscribe<TSource, TValue>(
            this TSource source,
            Expression<Func<TSource, TValue>> property,
            Action<TValue> observer,
            Action onCompleted)
        where TSource : INotifyPropertyChanged
    {
        return source
                .GetPropertyValues(property)
                .Subscribe(observer, onCompleted);
    }

    public static IDisposable
        Subscribe<TSource, TValue>(
            this TSource source,
            Expression<Func<TSource, TValue>> property,
            Action<TValue> observer,
            Action<Exception> onException)
        where TSource : INotifyPropertyChanged
    {
        return source
                .GetPropertyValues(property)
                .Subscribe(observer, onException);
    }

    public static IDisposable
        Subscribe<TSource, TValue>(
            this TSource source,
            Expression<Func<TSource, TValue>> property,
            Action<TValue> observer,
            Action<Exception> onException,
            Action onCompleted)
        where TSource : INotifyPropertyChanged
    {
        return source
                .GetPropertyValues(property)
                .Subscribe(observer, onException, onCompleted);
    }
}
Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

Why should you layer your APIs like this? Read on here...

The Introduction to Rx.NET (v6.1) 3rd Edition (2025) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

Matthew Adams on Twitter

Matthew Adams

Co-Founder

Matthew Adams

Matthew was CTO of a venture-backed technology start-up in the UK & US for 10 years, and is now the co-founder of endjin, which provides technology strategy, experience and development services to its customers who are seeking to take advantage of Microsoft Azure and the Cloud.