-
-
Save jrgcubano/1ba5a06962d4f89addf178a0a474b30c to your computer and use it in GitHub Desktop.
ObservePropertyChanged extension method (INotifyPropertyChanged.Property => IObservable)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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