Viewmodel property to observable stream

When implementing a user interface in WPF/MVVM applications we frequently encounter situations where we want to be able to react on all changes made to a particular property.

One way to handle this is to create an observable sequence of values with Reactive Extensions (Rx). This is especially valuable if we have other event streams that interact with the property changed events.

There are several libraries around that can help with this like e.g. ReactiveUI or ReactiveProperty.

But if we want a lightweight, generic, reusable, and typesafe solution, we can consider just using the following extension method, which can be called on any instance that implements INotifyPropertyChanged. It retrieves the property name from the expression that is passed in, converts all PropertyChanged events into an observable sequence, and then finds the value of the right property using reflection.

public static IObservable<T> ToObservable<T>(
    this INotifyPropertyChanged source, 
    Expression<Func<T>> propertyExpression)
{
    var memberExpression = propertyExpression.Body as MemberExpression;
    return memberExpression == null
        ? Observable.Empty<T>()
        : Observable
            .FromEventPattern<PropertyChangedEventArgs>(source, "PropertyChanged")
            .Where(e => e.EventArgs.PropertyName == memberExpression.Member.Name)
            .SelectMany(_ => source.GetType()
                .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Where(info => info.Name == memberExpression.Member.Name))
            .Select(info => (T)info.GetValue(source));
}

In the view, the control’s data binding has to have an UpdateSourceTrigger that is set to PropertyChanged:

<TextBox Text="{Binding Input, UpdateSourceTrigger=PropertyChanged}"/>

This is how the extension method could be used in a viewmodel e.g.:

public MainWindowViewModel()
{
    this.ToObservable(() => Input)
        .Subscribe(Output.Add);
}

In this example Input is bound to a TextBox control and Output is bound to a ListBox control. So on every change of Input the current value will be added to the list:

alt text

The complete source code of this example can be found here.

A useful application of this is also described in this blog post.