static IObservedChange <object, object> observedChangeFor(string propertyName, IObservedChange <object, object> sourceChange) { var p = new ObservedChange <object, object>() { Sender = sourceChange.Value, PropertyName = propertyName, }; if (sourceChange.Value == null) { return(p); } return(p.fillInValue()); }
/// <summary> /// WhenAny allows you to observe whenever one or more properties on an /// object have changed, providing an initial value when the Observable /// is set up, unlike ObservableForProperty(). Use this method in /// constructors to set up bindings between properties that also need an /// initial setup. /// </summary> public static IObservable <TRet> WhenAnyDynamic <TSender, TRet>(this TSender This, string[] property1, Func <IObservedChange <TSender, object>, TRet> selector) { var slot1 = new ObservedChange <TSender, object>() { Sender = This, PropertyName = String.Join(".", property1), }; object slot1Value = default(object); slot1.TryGetValue(out slot1Value); slot1.Value = slot1Value; IObservedChange <TSender, object> islot1 = slot1; return(Observable.Create <TRet>(subject => { subject.OnNext(selector(islot1)); return Observable.Merge(This.ObservableForProperty(property1).Do(x => { lock (slot1) { islot1 = x.fillInValue(); } }).Select(x => selector(islot1))).Subscribe(subject); })); }
/// <summary> /// WhenAny allows you to observe whenever one or more properties on an /// object have changed, providing an initial value when the Observable /// is set up, unlike ObservableForProperty(). Use this method in /// constructors to set up bindings between properties that also need an /// initial setup. /// </summary> public static IObservable <TRet> WhenAny <TSender, TRet, T1>(this TSender This, Expression <Func <TSender, T1> > property1, Func <IObservedChange <TSender, T1>, TRet> selector) { var slot1 = new ObservedChange <TSender, T1>() { Sender = This, PropertyName = String.Join(".", Reflection.ExpressionToPropertyNames(property1)), }; T1 slot1Value = default(T1); slot1.TryGetValue(out slot1Value); slot1.Value = slot1Value; IObservedChange <TSender, T1> islot1 = slot1; return(Observable.Create <TRet>(subject => { subject.OnNext(selector(islot1)); return Observable.Merge(This.ObservableForProperty(property1).Do(x => { lock (slot1) { islot1 = x.fillInValue(); } }).Select(x => selector(islot1))).Subscribe(subject); })); }
/// <summary> /// WhenAny allows you to observe whenever one or more properties on an /// object have changed, providing an initial value when the Observable /// is set up, unlike ObservableForProperty(). Use this method in /// constructors to set up bindings between properties that also need an /// initial setup. /// </summary> public static IObservable <TRet> WhenAny <TSender, TRet, T1, T2, T3>(this TSender This, Expression <Func <TSender, T1> > property1, Expression <Func <TSender, T2> > property2, Expression <Func <TSender, T3> > property3, Func <IObservedChange <TSender, T1>, IObservedChange <TSender, T2>, IObservedChange <TSender, T3>, TRet> selector) where TSender : IReactiveNotifyPropertyChanged { var slot1 = new ObservedChange <TSender, T1>() { Sender = This, PropertyName = String.Join(".", RxApp.expressionToPropertyNames(property1)), }; T1 slot1Value = default(T1); slot1.TryGetValue(out slot1Value); slot1.Value = slot1Value; IObservedChange <TSender, T1> islot1 = slot1; var slot2 = new ObservedChange <TSender, T2>() { Sender = This, PropertyName = String.Join(".", RxApp.expressionToPropertyNames(property2)), }; T2 slot2Value = default(T2); slot2.TryGetValue(out slot2Value); slot2.Value = slot2Value; IObservedChange <TSender, T2> islot2 = slot2; var slot3 = new ObservedChange <TSender, T3>() { Sender = This, PropertyName = String.Join(".", RxApp.expressionToPropertyNames(property3)), }; T3 slot3Value = default(T3); slot3.TryGetValue(out slot3Value); slot3.Value = slot3Value; IObservedChange <TSender, T3> islot3 = slot3; return(Observable.Create <TRet>(subject => { subject.OnNext(selector(islot1, islot2, islot3)); return Observable.Merge( This.ObservableForProperty(property1).Do(x => { lock (slot1) { islot1 = x.fillInValue(); } }).Select(x => selector(islot1, islot2, islot3)), This.ObservableForProperty(property2).Do(x => { lock (slot2) { islot2 = x.fillInValue(); } }).Select(x => selector(islot1, islot2, islot3)), This.ObservableForProperty(property3).Do(x => { lock (slot3) { islot3 = x.fillInValue(); } }).Select(x => selector(islot1, islot2, islot3)) ).Subscribe(subject); })); }
public static bool TryGetAllValuesForPropertyChain(out IObservedChange <object, object>[] changeValues, object current, string[] propNames) { int currentIndex = 0; changeValues = new IObservedChange <object, object> [propNames.Length]; foreach (var propName in propNames.SkipLast(1)) { if (current == null) { changeValues[currentIndex] = null; return(false); } var box = new ObservedChange <object, object> { Sender = current, PropertyName = propName }; current = GetValueFetcherOrThrow(current.GetType(), propName)(current); box.Value = current; changeValues[currentIndex] = box; currentIndex++; } if (current == null) { changeValues[currentIndex] = null; return(false); } changeValues[currentIndex] = new ObservedChange <object, object> { Sender = current, PropertyName = propNames.Last(), Value = GetValueFetcherOrThrow(current.GetType(), propNames.Last())(current) }; return(true); }
static void subscribeToExpressionChain <TSender, TValue>( TSender origSource, string origPath, object source, LinkedListNode <string> propertyNames, LinkedListNode <IDisposable> subscriptions, bool beforeChange, Subject <IObservedChange <TSender, TValue> > subject ) { var current = propertyNames; var currentSub = subscriptions; object currentObj = source; ObservedChange <TSender, TValue> obsCh; while (current.Next != null) { Func <object, object> getter = null; if (currentObj != null) { getter = Reflection.GetValueFetcherForProperty(currentObj.GetType(), current.Value); if (getter == null) { subscriptions.List.Where(x => x != null).ForEach(x => x.Dispose()); throw new ArgumentException(String.Format("Property '{0}' does not exist in expression", current.Value)); } var capture = new { current, currentObj, getter, currentSub }; var toDispose = new IDisposable[2]; var valGetter = new ObservedChange <object, TValue>() { Sender = capture.currentObj, PropertyName = buildPropPathFromNodePtr(capture.current), Value = default(TValue), }; TValue prevVal = default(TValue); bool prevValSet = valGetter.TryGetValue(out prevVal); toDispose[0] = notifyForProperty(currentObj, capture.current.Value, true).Subscribe(x => { prevValSet = valGetter.TryGetValue(out prevVal); }); toDispose[1] = notifyForProperty(currentObj, capture.current.Value, false).Subscribe(x => { subscribeToExpressionChain(origSource, origPath, capture.getter(capture.currentObj), capture.current.Next, capture.currentSub.Next, beforeChange, subject); TValue newVal; if (!valGetter.TryGetValue(out newVal)) { return; } if (prevValSet && EqualityComparer <TValue> .Default.Equals(prevVal, newVal)) { return; } obsCh = new ObservedChange <TSender, TValue>() { Sender = origSource, PropertyName = origPath, Value = default(TValue), }; TValue obsChVal; if (obsCh.TryGetValue(out obsChVal)) { obsCh.Value = obsChVal; subject.OnNext(obsCh); } }); currentSub.Value = Disposable.Create(() => { toDispose[0].Dispose(); toDispose[1].Dispose(); }); } current = current.Next; currentSub = currentSub.Next; currentObj = getter != null?getter(currentObj) : null; } if (currentSub.Value != null) { currentSub.Value.Dispose(); } if (currentObj == null) { return; } var propName = current.Value; var finalGetter = Reflection.GetValueFetcherForProperty(currentObj.GetType(), current.Value); currentSub.Value = notifyForProperty(currentObj, propName, beforeChange).Subscribe(x => { obsCh = new ObservedChange <TSender, TValue>() { Sender = origSource, PropertyName = origPath, Value = (TValue)finalGetter(currentObj), }; subject.OnNext(obsCh); }); }
static void subscribeToExpressionChain <TSender, TValue>( TSender origSource, string origPath, object source, LinkedListNode <string> propertyNames, LinkedListNode <IDisposable> subscriptions, bool beforeChange, Subject <IObservedChange <TSender, TValue> > subject ) { var current = propertyNames; var currentSub = subscriptions; object currentObj = source; PropertyInfo pi = null; ObservedChange <TSender, TValue> obsCh; while (current.Next != null) { pi = RxApp.getPropertyInfoForProperty(currentObj.GetType(), current.Value); if (pi == null) { subscriptions.List.Where(x => x != null).ForEach(x => x.Dispose()); throw new ArgumentException(String.Format("Property '{0}' does not exist in expression", current.Value)); } var notifyObj = wrapInpcObjectIfNeeded(currentObj); if (notifyObj != null) { var capture = new { current, currentObj, pi, currentSub }; var toDispose = new IDisposable[2]; var valGetter = new ObservedChange <object, TValue>() { Sender = capture.currentObj, PropertyName = buildPropPathFromNodePtr(capture.current), Value = default(TValue), }; TValue prevVal = default(TValue); bool prevValSet = valGetter.TryGetValue(out prevVal); toDispose[0] = notifyObj.Changing.Where(x => x.PropertyName == capture.current.Value).Subscribe(x => { prevValSet = valGetter.TryGetValue(out prevVal); }); toDispose[1] = notifyObj.Changed.Where(x => x.PropertyName == capture.current.Value).Subscribe(x => { subscribeToExpressionChain(origSource, origPath, capture.pi.GetValue(capture.currentObj, null), capture.current.Next, capture.currentSub.Next, beforeChange, subject); TValue newVal; if (!valGetter.TryGetValue(out newVal)) { return; } if (prevValSet && EqualityComparer <TValue> .Default.Equals(prevVal, newVal)) { return; } obsCh = new ObservedChange <TSender, TValue>() { Sender = origSource, PropertyName = origPath, Value = default(TValue), }; TValue obsChVal; if (obsCh.TryGetValue(out obsChVal)) { obsCh.Value = obsChVal; subject.OnNext(obsCh); } }); currentSub.Value = Disposable.Create(() => { toDispose[0].Dispose(); toDispose[1].Dispose(); }); } current = current.Next; currentSub = currentSub.Next; currentObj = pi.GetValue(currentObj, null); } var finalNotify = wrapInpcObjectIfNeeded(currentObj); if (currentSub.Value != null) { currentSub.Value.Dispose(); } if (finalNotify == null) { return; } var propName = current.Value; pi = RxApp.getPropertyInfoForProperty(currentObj.GetType(), current.Value); currentSub.Value = (beforeChange ? finalNotify.Changing : finalNotify.Changed).Where(x => x.PropertyName == propName).Subscribe(x => { obsCh = new ObservedChange <TSender, TValue>() { Sender = origSource, PropertyName = origPath, Value = (TValue)pi.GetValue(currentObj, null), }; subject.OnNext(obsCh); }); }