RX and INotifyPropertyChanged
November 2, 2010 by Matthew Adams
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);
}
}
Why should you layer your APIs like this? Read on here…
Matthew Adams on Twitter
If you enjoyed this post, please consider sharing it on social media using the buttons below. Thanks!
About the author
Matthew was CTO of a venture-backed technology start-up in the UK & US for 10 years, and is now a Founder of Endjin Ltd, which provides technology strategy, experience and development services to its clients who are seeking to take advantage of Microsoft Azure and the Cloud.
You can follow Matthew on twitter.
This entry was posted in Culture and tagged