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());
        }
예제 #2
0
        /// <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);
            }));
        }
예제 #3
0
        /// <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);
            }));
        }
예제 #4
0
        /// <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);
            }));
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
            });
        }
예제 #7
0
        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);
            });
        }