Skip to content

Instantly share code, notes, and snippets.

@robfe
Created October 11, 2010 08:45
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save robfe/620226 to your computer and use it in GitHub Desktop.
Save robfe/620226 to your computer and use it in GitHub Desktop.
ObservePropertyChanged extension method (INotifyPropertyChanged.Property => IObservable)
public static class Extensions
{
public static IObservable<TProperty> ObservePropertyChanged<TNotifier, TProperty>(
this TNotifier notifier,
Expression<Func<TNotifier, TProperty>> propertyAccessor,
bool startWithCurrent = false)
where TNotifier : INotifyPropertyChanged
{
// Parse the expression to find the correct property name.
MemberExpression member = (MemberExpression)propertyAccessor.Body;
string name = member.Member.Name;
// Compile the expression so we can run it to read the property value.
var reader = propertyAccessor.Compile();
var propertyChanged = Observable.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
handler => (sender, args) => handler(sender, args),
x => notifier.PropertyChanged += x,
x => notifier.PropertyChanged -= x);
// Filter the events to the correct property name, then select the value of the property from the notifier.
var newValues = from p in propertyChanged
where p.EventArgs.PropertyName == name
select reader(notifier);
// If the caller wants the current value as well as future ones, use Defer() so that the current value is read when the subscription
// is added, rather than right now. Otherwise just return the above observable.
return startWithCurrent ? Observable.Defer(() => Observable.Return(reader(notifier)).Concat(newValues)) : newValues;
}
}
[TestClass]
public class ExtensionsTests
{
[TestMethod]
public void ObserveFuturePropertyChanges()
{
PropertyChanger p = new PropertyChanger { Amount = 6 };
var observable = p.ObservePropertyChanged(x => x.Amount);
p.Amount = 0;
var ints1 = Store(observable);
p.Amount = 1;
var ints2 = Store(observable);
p.Amount = 2;
p.Amount = 3;
AssertEnumerablesEqual(new []{1,2,3}, ints1);
AssertEnumerablesEqual(new []{2,3}, ints2);
ints1.Dispose();
ints2.Dispose();
}
[TestMethod]
public void ObserveCurrentAndFuturePropertyChanges()
{
PropertyChanger p = new PropertyChanger { Amount = 6 };
var observable = p.ObservePropertyChanged(x => x.Amount, true);
p.Amount = 0;
var ints1 = Store(observable);
p.Amount = 1;
var ints2 = Store(observable);
p.Amount = 2;
p.Amount = 3;
AssertEnumerablesEqual(new[] { 0, 1, 2, 3 }, ints1);
AssertEnumerablesEqual(new[] { 1, 2, 3 }, ints2);
ints1.Dispose();
ints2.Dispose();
}
public static void AssertEnumerablesEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual)
{
if (!expected.SequenceEqual(actual))
{
Assert.Fail(string.Format("Expected [{0}], but was [{1}]", string.Join(", ", expected), string.Join(", ", actual)));
}
}
public class PropertyChanger : INotifyPropertyChanged
{
int amount;
public int Amount
{
get { return amount; }
set
{
amount = value;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs("Amount"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
@robfe
Copy link
Author

robfe commented Oct 11, 2010

Creates an IObservable of the future (and optionally current) values of an INPC property

@MarcelMalik
Copy link

Can you provide the source for the Store(..) method?

Thx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment