Esempio n. 1
0
        /// <summary>
        /// Attempts to return the current value of a property given a
        /// notification that it has changed. If any property in the
        /// property expression is null, false is returned.
        /// </summary>
        /// <param name="This">
        /// The <see cref="IObservedChange{TSender, TValue}"/> instance to get the value of
        /// </param>
        /// <param name="changeValue">
        /// The value of the property expression.
        /// </param>
        /// <returns>
        /// True if the entire expression was able to be followed, false otherwise.
        /// </returns>
        internal static bool TryGetValue <TSender, TValue>(this IObservedChange <TSender, TValue> This, out TValue changeValue)
        {
            if (!Equals(This.Value, default(TValue)))
            {
                changeValue = This.Value;
                return(true);
            }

            return(Reflection.TryGetValueForPropertyChain(out changeValue, This.Sender, This.Expression.GetExpressionChain()));
        }
Esempio n. 2
0
        /// <summary>
        /// Attempts to return the current value of a property given a
        /// notification that it has changed. If any property in the
        /// property expression is null, false is returned.
        /// </summary>
        /// <param name="changeValue">The value of the property
        /// expression.</param>
        /// <returns>True if the entire expression was able to be followed,
        /// false otherwise</returns>
        public static bool TryGetValue <TSender, TValue>(this IObservedChange <TSender, TValue> This, out TValue changeValue)
        {
            if (!Equals(This.Value, default(TValue)))
            {
                changeValue = This.Value;
                return(true);
            }

            object current      = This.Sender;
            string fullPropName = This.PropertyName;

            return(Reflection.TryGetValueForPropertyChain(out changeValue, current, fullPropName.Split('.')));
        }
        /// <summary>
        /// Attempts to return the current value of a property given a
        /// notification that it has changed. If any property in the
        /// property expression is null, false is returned.
        /// </summary>
        /// <typeparam name="TSender">The sender type.</typeparam>
        /// <typeparam name="TValue">The value type.</typeparam>
        /// <param name="item">
        /// The <see cref="IObservedChange{TSender, TValue}"/> instance to get the value of.
        /// </param>
        /// <param name="changeValue">
        /// The value of the property expression.
        /// </param>
        /// <returns>
        /// True if the entire expression was able to be followed, false otherwise.
        /// </returns>
        internal static bool TryGetValue <TSender, TValue>(this IObservedChange <TSender, TValue> item, out TValue changeValue)
        {
            if (Equals(item.Sender, null))
            {
                throw new ArgumentNullException(nameof(item), "Sender of the item is null");
            }

            if (!Equals(item.Value, default(TValue)))
            {
                changeValue = item.Value;
                return(true);
            }

            return(Reflection.TryGetValueForPropertyChain(out changeValue, item.Sender, item.Expression.GetExpressionChain()));
        }
        static IObservedChange <object, object> observedChangeFor(Expression expression, IObservedChange <object, object> sourceChange)
        {
            var propertyName = expression.GetMemberInfo().Name;

            if (sourceChange.Value == null)
            {
                return(new ObservedChange <object, object>(sourceChange.Value, expression));;
            }
            else
            {
                object value;
                // expression is always a simple expression
                Reflection.TryGetValueForPropertyChain(out value, sourceChange.Value, new[] { expression });
                return(new ObservedChange <object, object>(sourceChange.Value, expression, value));
            }
        }
Esempio n. 5
0
        private IReactiveBinding <TView, TViewModel, Tuple <object, bool> > BindImpl <TViewModel, TView, TVMProp, TVProp, TDontCare>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TVMProp> > vmProperty,
            Expression <Func <TView, TVProp> > viewProperty,
            IObservable <TDontCare> signalViewUpdate,
            OutFunc <TVMProp, TVProp> vmToViewConverter,
            OutFunc <TVProp, TVMProp> viewToVmConverter)
            where TView : class, IViewFor
            where TViewModel : class
        {
            var signalInitialUpdate = new Subject <bool>();
            var vmExpression        = Reflection.Rewrite(vmProperty.Body);
            var viewExpression      = Reflection.Rewrite(viewProperty.Body);

            var signalObservable = signalViewUpdate != null
                                       ? signalViewUpdate.Select(_ => false)
                                       : view.WhenAnyDynamic(viewExpression, x => (TVProp)x.Value).Select(_ => false);

            var somethingChanged = Observable.Merge(
                Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(_ => true),
                signalInitialUpdate.Select(_ => true),
                signalObservable);

            var changeWithValues = somethingChanged.Select(isVm =>
            {
                TVMProp vmValue;
                TVProp vValue;
                if (!Reflection.TryGetValueForPropertyChain(out vmValue, view.ViewModel, vmExpression.GetExpressionChain()) ||
                    !Reflection.TryGetValueForPropertyChain(out vValue, view, viewExpression.GetExpressionChain()))
                {
                    return(null);
                }

                if (isVm)
                {
                    TVProp vmAsView;
                    if (!vmToViewConverter(vmValue, out vmAsView) || EqualityComparer <TVProp> .Default.Equals(vValue, vmAsView))
                    {
                        return(null);
                    }

                    return(Tuple.Create((object)vmAsView, isVm));
                }

                TVMProp vAsViewModel;
                if (!viewToVmConverter(vValue, out vAsViewModel) || EqualityComparer <TVMProp> .Default.Equals(vmValue, vAsViewModel))
                {
                    return(null);
                }

                return(Tuple.Create((object)vAsViewModel, isVm));
            });

            var ret = EvalBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.TwoWay);

            if (!ret)
            {
                return(null);
            }

            IObservable <Tuple <object, bool> > changes = changeWithValues.Where(tuple => tuple != null).Publish().RefCount();

            IDisposable disp = changes.Subscribe(isVmWithLatestValue =>
            {
                if (isVmWithLatestValue.Item2)
                {
                    Reflection.TrySetValueToPropertyChain(view, viewExpression.GetExpressionChain(), isVmWithLatestValue.Item1, false);
                }
                else
                {
                    Reflection.TrySetValueToPropertyChain(view.ViewModel, vmExpression.GetExpressionChain(), isVmWithLatestValue.Item1, false);
                }
            });

            // NB: Even though it's technically a two-way bind, most people
            // want the ViewModel to win at first.
            signalInitialUpdate.OnNext(true);

            return(new ReactiveBinding <TView, TViewModel, Tuple <object, bool> >(
                       view,
                       viewModel,
                       viewExpression,
                       vmExpression,
                       changes,
                       BindingDirection.TwoWay,
                       disp));
        }
Esempio n. 6
0
        public IDisposable Bind <TViewModel, TView, TVMProp, TVProp, TDontCare>(
            TViewModel viewModel,
            TView view,
            Expression <Func <TViewModel, TVMProp> > vmProperty,
            Expression <Func <TView, TVProp> > viewProperty,
            IObservable <TDontCare> signalViewUpdate,
            object conversionHint)
            where TViewModel : class
            where TView : IViewFor
        {
            var signalInitialUpdate = new Subject <bool>();
            var vmPropChain         = Reflection.ExpressionToPropertyNames(vmProperty);

            string[] viewPropChain;

            if (viewProperty == null)
            {
                viewPropChain = Reflection.getDefaultViewPropChain(view, vmPropChain);
            }
            else
            {
                viewPropChain = Reflection.ExpressionToPropertyNames(viewProperty);
            }

            var vmToViewConverter = getConverterForTypes(typeof(TVMProp), typeof(TVProp));
            var viewToVMConverter = getConverterForTypes(typeof(TVProp), typeof(TVMProp));

            if (vmToViewConverter == null || viewToVMConverter == null)
            {
                throw new ArgumentException(
                          String.Format("Can't two-way convert between {0} and {1}. To fix this, register a IBindingTypeConverter", typeof(TVMProp), typeof(TVProp)));
            }

            var somethingChanged = Observable.Merge(
                Reflection.ViewModelWhenAnyValue(viewModel, view, vmProperty).Select(_ => true),
                signalInitialUpdate,
                signalViewUpdate != null ?
                signalViewUpdate.Select(_ => false) :
                view.WhenAnyDynamic(viewPropChain, x => (TVProp)x.Value).Select(_ => false));

            string vmChangedString = String.Format("Setting {0}.{1} => {2}.{3}: ",
                                                   typeof(TViewModel).Name, String.Join(".", vmPropChain),
                                                   typeof(TView).Name, String.Join(".", viewPropChain));

            string viewChangedString = String.Format("Setting {0}.{1} => {2}.{3}: ",
                                                     typeof(TView).Name, String.Join(".", viewPropChain),
                                                     typeof(TViewModel).Name, String.Join(".", vmPropChain));

            var changeWithValues = somethingChanged.Select(isVm => {
                TVMProp vmValue; TVProp vValue;
                if (!Reflection.TryGetValueForPropertyChain(out vmValue, view.ViewModel, vmPropChain) ||
                    !Reflection.TryGetValueForPropertyChain(out vValue, view, viewPropChain))
                {
                    return(null);
                }

                if (isVm)
                {
                    var vmAsView = (TVProp)vmToViewConverter.Convert(vmValue, typeof(TVProp), conversionHint);
                    var changed  = EqualityComparer <TVProp> .Default.Equals(vValue, vmAsView) != true;
                    if (!changed)
                    {
                        return(null);
                    }

                    this.Log().Info(vmChangedString + (vmAsView != null ? vmAsView.ToString() : "(null)"));
                    return(Tuple.Create((object)vmAsView, isVm));
                }
                else
                {
                    var vAsViewModel = (TVMProp)viewToVMConverter.Convert(vValue, typeof(TVMProp), conversionHint);
                    var changed      = EqualityComparer <TVMProp> .Default.Equals(vmValue, vAsViewModel) != true;
                    if (!changed)
                    {
                        return(null);
                    }

                    this.Log().Info(viewChangedString + (vAsViewModel != null ? vAsViewModel.ToString() : "(null)"));
                    return(Tuple.Create((object)vAsViewModel, isVm));
                }
            });

            var ret = changeWithValues.Subscribe(isVmWithLatestValue => {
                if (isVmWithLatestValue == null)
                {
                    return;
                }

                if (isVmWithLatestValue.Item2)
                {
                    Reflection.SetValueToPropertyChain(view, viewPropChain, isVmWithLatestValue.Item1, false);
                }
                else
                {
                    Reflection.SetValueToPropertyChain(view.ViewModel, vmPropChain, isVmWithLatestValue.Item1, false);
                }
            });

            // NB: Even though it's technically a two-way bind, most people
            // want the ViewModel to win at first.
            signalInitialUpdate.OnNext(true);

            return(ret);
        }