Created
October 11, 2010 08:45
-
-
Save robfe/620226 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; | |
} | |
} |
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
Creates an IObservable of the future (and optionally current) values of an INPC property