diff --git a/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs b/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs index 9993b491..22232e7a 100644 --- a/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs +++ b/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs @@ -35,7 +35,39 @@ public static IObservable WhenPropertyValueChanges( throw new ArgumentNullException(nameof(propertyExpression)); } - return WhenPropertyChanges(objectToMonitor, propertyExpression).Select(x => x.Value); + IObservable currentObservable = Observable.Return((INotifyPropertyChanged)objectToMonitor); + + var expressionChain = propertyExpression.Body.GetExpressionChain(); + + if (expressionChain.Count == 0) + { + throw new ArgumentException("There are no properties in the expressions", nameof(propertyExpression)); + } + + var i = 0; + foreach (var memberExpression in expressionChain) + { + var memberInfo = memberExpression.Member; + + if (i == expressionChain.Count - 1) + { + return Observable.Return(memberInfo) + .CombineLatest(currentObservable, (memberInfo, parent) => (memberInfo, value: parent)) + .Where(x => x.value != null) + .Select(x => GenerateObservable(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) + .Switch(); + } + + currentObservable = Observable.Return(memberInfo) + .CombineLatest(currentObservable, (memberInfo, parent) => (memberInfo, value: parent)) + .Where(x => x.value != null) + .Select(x => GenerateObservable(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) + .Switch(); + + i++; + } + + throw new ArgumentException("Invalid expression", nameof(propertyExpression)); } /// @@ -77,14 +109,14 @@ public static IObservable WhenPropertyValueChanges( return Observable.Return(memberInfo) .CombineLatest(currentObservable, (memberInfo, parent) => (memberInfo, sender: parent.Sender, value: parent.Value)) .Where(x => x.value != null) - .Select(x => GenerateObservable(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) + .Select(x => GenerateObservableWithSender(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) .Switch(); } currentObservable = Observable.Return(memberInfo) .CombineLatest(currentObservable, (memberInfo, parent) => (memberInfo, sender: parent.Sender, value: parent.Value)) .Where(x => x.value != null) - .Select(x => GenerateObservable(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) + .Select(x => GenerateObservableWithSender(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) .Switch(); i++; @@ -93,22 +125,51 @@ public static IObservable WhenPropertyValueChanges( throw new ArgumentException("Invalid expression", nameof(propertyExpression)); } - private static IObservable<(object Sender, T Value)> GenerateObservable( + private static IObservable GenerateObservable( INotifyPropertyChanged parent, MemberInfo memberInfo, Func getter) { var memberName = memberInfo.Name; - return Observable.FromEvent( + return Observable.FromEvent( handler => { - void Handler(object sender, PropertyChangedEventArgs e) => handler((sender, e)); + void Handler(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == memberName) + { + handler(getter(parent)); + } + } + + return Handler; + }, + x => parent.PropertyChanged += x, + x => parent.PropertyChanged -= x) + .StartWith(getter(parent)); + } + + private static IObservable<(object Sender, T Value)> GenerateObservableWithSender( + INotifyPropertyChanged parent, + MemberInfo memberInfo, + Func getter) + { + var memberName = memberInfo.Name; + return Observable.FromEvent( + handler => + { + void Handler(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == memberName) + { + handler((sender, getter(parent))); + } + } + return Handler; }, x => parent.PropertyChanged += x, x => parent.PropertyChanged -= x) - .Where(x => x.Args.PropertyName == memberName) - .Select(x => (x.Sender, getter(parent))) .StartWith((parent, getter(parent))); } }