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.
Pingback: endjin blog » Blog Archive » RX and INotifyPropertyChanged
Cool post – having strongly typed Observables for properties definitely makes wiring things up easier. I wrote a helper library to do a lot of this stuff for free called ReactiveXaml (http://github.com/xpaulbettsx/ReactiveXaml), it has an almost syntactically identical method called ObservableForProperty (an example is at http://pastie.org/1266891)
Pingback: endjin blog » Blog Archive » Layering your API
Pingback: Layering your API | endjin blog
Pingback: RX and INotifyPropertyChanged | endjin blog