Skip to content

Instantly share code, notes, and snippets.

@jrgcubano
Forked from robfe/Extensions.cs
Created May 19, 2016 09:56
Show Gist options
  • Save jrgcubano/1ba5a06962d4f89addf178a0a474b30c to your computer and use it in GitHub Desktop.
Save jrgcubano/1ba5a06962d4f89addf178a0a474b30c 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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment