Ejemplo n.º 1
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>(this TSender This,
                                                                         Expression <Func <TSender, T1> > property1,
                                                                         Expression <Func <TSender, T2> > property2,
                                                                         Func <IObservedChange <TSender, T1>, IObservedChange <TSender, T2>, 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;


            return(Observable.Create <TRet>(subject => {
                subject.OnNext(selector(islot1, islot2));

                return Observable.Merge(
                    This.ObservableForProperty(property1).Do(x => { lock (slot1) { islot1 = x.fillInValue(); } }).Select(x => selector(islot1, islot2)),
                    This.ObservableForProperty(property2).Do(x => { lock (slot2) { islot2 = x.fillInValue(); } }).Select(x => selector(islot1, islot2))
                    ).Subscribe(subject);
            }));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// BindTo takes an Observable stream and applies it to a target
        /// property. Conceptually it is similar to "Subscribe(x =&gt;
        /// target.property = x)", but allows you to use child properties
        /// without the null checks.
        /// </summary>
        /// <param name="target">The target object whose property will be set.</param>
        /// <param name="property">An expression representing the target
        /// property to set. This can be a child property (i.e. x.Foo.Bar.Baz).</param>
        /// <returns>An object that when disposed, disconnects the binding.</returns>
        public static IDisposable BindTo <TTarget, TValue>(
            this IObservable <TValue> This,
            TTarget target,
            Expression <Func <TTarget, TValue> > property)
            where TTarget : IReactiveNotifyPropertyChanged
        {
            var sourceSub = new MultipleAssignmentDisposable();
            var source    = This;

            var subscribify = new Action <TTarget, string[]>((tgt, propNames) => {
                if (sourceSub.Disposable != null)
                {
                    sourceSub.Disposable.Dispose();
                }

                object current  = tgt;
                PropertyInfo pi = null;
                foreach (var propName in propNames.SkipLast(1))
                {
                    if (current == null)
                    {
                        return;
                    }

                    pi      = RxApp.getPropertyInfoOrThrow(current.GetType(), propName);
                    current = pi.GetValue(current, null);
                }
                if (current == null)
                {
                    return;
                }

                pi = RxApp.getPropertyInfoOrThrow(current.GetType(), propNames.Last());
                sourceSub.Disposable = This.Subscribe(x => {
                    pi.SetValue(current, x, null);
                });
            });

            IDisposable[] toDispose     = new IDisposable[] { sourceSub, null };
            string[]      propertyNames = RxApp.expressionToPropertyNames(property);
            toDispose[1] = target.ObservableForProperty(property).Subscribe(_ => subscribify(target, propertyNames));

            subscribify(target, propertyNames);

            return(Disposable.Create(() => { toDispose[0].Dispose(); toDispose[1].Dispose(); }));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// ObservableForProperty returns an Observable representing the
        /// property change notifications for a specific property on a
        /// ReactiveObject. This method (unlike other Observables that return
        /// IObservedChange) guarantees that the Value property of
        /// the IObservedChange is set.
        /// </summary>
        /// <param name="property">An Expression representing the property (i.e.
        /// 'x => x.SomeProperty.SomeOtherProperty'</param>
        /// <param name="beforeChange">If True, the Observable will notify
        /// immediately before a property is going to change.</param>
        /// <returns>An Observable representing the property change
        /// notifications for the given property.</returns>
        public static IObservable <IObservedChange <TSender, TValue> > ObservableForProperty <TSender, TValue>(
            this TSender This,
            Expression <Func <TSender, TValue> > property,
            bool beforeChange = false)
            where TSender : INotifyPropertyChanged
        {
            var propertyNames = new LinkedList <string>(RxApp.expressionToPropertyNames(property));
            var subscriptions = new LinkedList <IDisposable>(propertyNames.Select(x => (IDisposable)null));
            var ret           = new Subject <IObservedChange <TSender, TValue> >();

            /* x => x.Foo.Bar.Baz;
             *
             * Subscribe to This, look for Foo
             * Subscribe to Foo, look for Bar
             * Subscribe to Bar, look for Baz
             * Subscribe to Baz, publish to Subject
             * Return Subject
             *
             * If Bar changes (notification fires on Foo), resubscribe to new Bar
             *  Resubscribe to new Baz, publish to Subject
             *
             * If Baz changes (notification fires on Bar),
             *  Resubscribe to new Baz, publish to Subject
             */

            subscribeToExpressionChain(
                This,
                buildPropPathFromNodePtr(propertyNames.First),
                This,
                propertyNames.First,
                subscriptions.First,
                beforeChange,
                ret);

            return(Observable.Create <IObservedChange <TSender, TValue> >(x => {
                var disp = ret.Subscribe(x.OnNext, x.OnError, x.OnCompleted);
                return () => {
                    subscriptions.ForEach(y => y.Dispose());
                    disp.Dispose();
                };
            }));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Given a fully filled-out IObservedChange object, SetValueToProperty
        /// will apply it to the specified object (i.e. it will ensure that
        /// target.property == This.GetValue() and "replay" the observed change
        /// onto another object)
        /// </summary>
        /// <param name="target">The target object to apply the change to.</param>
        /// <param name="property">The target property to apply the change to.</param>
        public static void SetValueToProperty <TSender, TValue, TTarget>(
            this IObservedChange <TSender, TValue> This,
            TTarget target,
            Expression <Func <TTarget, TValue> > property)
        {
            object current = target;

            string[] propNames = RxApp.expressionToPropertyNames(property);

            PropertyInfo pi;

            foreach (var propName in propNames.SkipLast(1))
            {
                pi      = RxApp.getPropertyInfoOrThrow(current.GetType(), propName);
                current = pi.GetValue(current, null);
            }

            pi = RxApp.getPropertyInfoForProperty(current.GetType(), propNames.Last());
            pi.SetValue(current, This.GetValue(), null);
        }