Exemplo n.º 1
0
        /// <summary>
        /// Applies a binding subject to a property on an instance.
        /// </summary>
        /// <param name="target">The target instance.</param>
        /// <param name="property">The target property.</param>
        /// <param name="subject">The binding subject.</param>
        internal void Bind(IPerspexObject target, PerspexProperty property, ISubject <object> subject)
        {
            var mode = Mode == BindingMode.Default ?
                       property.GetMetadata(target.GetType()).DefaultBindingMode : Mode;

            switch (mode)
            {
            case BindingMode.Default:
            case BindingMode.OneWay:
                target.Bind(property, subject, Priority);
                break;

            case BindingMode.TwoWay:
                throw new NotSupportedException("TwoWay MultiBinding not currently supported.");

            case BindingMode.OneTime:
                target.GetObservable(Control.DataContextProperty).Subscribe(dataContext =>
                {
                    subject.Take(1).Subscribe(x => target.SetValue(property, x, Priority));
                });
                break;

            case BindingMode.OneWayToSource:
                target.GetObservable(property).Subscribe(subject);
                break;
            }
        }
Exemplo n.º 2
0
        /// <inheritdoc/>
        public InstancedBinding Initiate(
            IPerspexObject target,
            PerspexProperty targetProperty,
            object anchor = null)
        {
            if (Name == "Red")
            {
            }

            var host     = (target as IControl) ?? (anchor as IControl);
            var style    = anchor as IStyle;
            var resource = PerspexProperty.UnsetValue;

            if (host != null)
            {
                resource = host.FindStyleResource(Name);
            }
            else if (style != null)
            {
                resource = style.FindResource(Name);
            }

            if (resource != PerspexProperty.UnsetValue)
            {
                return(new InstancedBinding(resource, Priority));
            }
            else
            {
                return(null);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets an observable for a <see cref="PerspexProperty"/>.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <typeparam name="T">The type of the property.</typeparam>
        /// <param name="property">The property.</param>
        /// <returns>
        /// An observable which when subscribed pushes the old and new values of the property each
        /// time it is changed.
        /// </returns>
        public static IObservable <Tuple <T, T> > GetObservableWithHistory <T>(
            this IPerspexObject o,
            PerspexProperty <T> property)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(property != null);

            return(new PerspexObservable <Tuple <T, T> >(
                       observer =>
            {
                EventHandler <PerspexPropertyChangedEventArgs> handler = (s, e) =>
                {
                    if (e.Property == property)
                    {
                        observer.OnNext(Tuple.Create((T)e.OldValue, (T)e.NewValue));
                    }
                };

                o.PropertyChanged += handler;

                return Disposable.Create(() =>
                {
                    o.PropertyChanged -= handler;
                });
            },
                       GetDescription(o, property)));
        }
Exemplo n.º 4
0
        /// <summary>
        /// Gets an observable for a <see cref="PerspexProperty"/>.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="property">The property.</param>
        /// <returns>An observable.</returns>
        public static IObservable <object> GetObservable(this IPerspexObject o, PerspexProperty property)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(property != null);

            return(new PerspexObservable <object>(
                       observer =>
            {
                EventHandler <PerspexPropertyChangedEventArgs> handler = (s, e) =>
                {
                    if (e.Property == property)
                    {
                        observer.OnNext(e.NewValue);
                    }
                };

                observer.OnNext(o.GetValue(property));

                o.PropertyChanged += handler;

                return Disposable.Create(() =>
                {
                    o.PropertyChanged -= handler;
                });
            },
                       GetDescription(o, property)));
        }
Exemplo n.º 5
0
        /// <summary>
        /// Gets an observable for a <see cref="PerspexProperty"/>.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <typeparam name="T">The property type.</typeparam>
        /// <param name="property">The property.</param>
        /// <returns>An observable.</returns>
        public static IObservable <T> GetObservable <T>(this IPerspexObject o, PerspexProperty <T> property)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(property != null);

            return(o.GetObservable((PerspexProperty)property).Cast <T>());
        }
Exemplo n.º 6
0
        /// <summary>
        /// Binds a property to a subject according to a <see cref="BindingMode"/>.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="property">The property to bind.</param>
        /// <param name="source">The binding source.</param>
        /// <param name="mode">The binding mode.</param>
        /// <param name="priority">The binding priority.</param>
        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
        public static IDisposable Bind(
            this IPerspexObject o,
            PerspexProperty property,
            ISubject <object> source,
            BindingMode mode,
            BindingPriority priority = BindingPriority.LocalValue)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(property != null);
            Contract.Requires <ArgumentNullException>(source != null);

            switch (mode)
            {
            case BindingMode.Default:
            case BindingMode.OneWay:
                return(o.Bind(property, source, priority));

            case BindingMode.TwoWay:
                return(new CompositeDisposable(
                           o.Bind(property, source, priority),
                           o.GetObservable(property).Subscribe(source)));

            case BindingMode.OneTime:
                return(source.Take(1).Subscribe(x => o.SetValue(property, x, priority)));

            case BindingMode.OneWayToSource:
                return(o.GetObservable(property).Subscribe(source));

            default:
                throw new ArgumentException("Invalid binding mode.");
            }
        }
Exemplo n.º 7
0
        private ExpressionObserver CreateDataContexObserver(
            IPerspexObject target,
            string path,
            bool targetIsDataContext)
        {
            Contract.Requires <ArgumentNullException>(target != null);

            if (!targetIsDataContext)
            {
                var update = target.GetObservable(Control.DataContextProperty)
                             .Skip(1)
                             .Select(_ => Unit.Default);
                var result = new ExpressionObserver(
                    () => target.GetValue(Control.DataContextProperty),
                    path,
                    update);

                return(result);
            }
            else
            {
                return(new ExpressionObserver(
                           target.GetObservable(Visual.VisualParentProperty)
                           .OfType <IPerspexObject>()
                           .Select(x => x.GetObservable(Control.DataContextProperty))
                           .Switch(),
                           path));
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Applies the binding to a property on an instance.
        /// </summary>
        /// <param name="instance">The target instance.</param>
        /// <param name="property">The target property.</param>
        public void Bind(IPerspexObject instance, PerspexProperty property)
        {
            var subject = CreateSubject(instance, property);

            if (subject != null)
            {
                Bind(instance, property, subject);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Applies the binding to a property on an instance.
        /// </summary>
        /// <param name="instance">The target instance.</param>
        /// <param name="property">The target property.</param>
        public void Bind(IPerspexObject instance, PerspexProperty property)
        {
            var subject = CreateSubject(instance, property);

            if (subject != null)
            {
                Bind(instance, property, subject);
            }
        }
Exemplo n.º 10
0
        /// <inheritdoc/>
        void IDirectPropertyAccessor.SetValue(IPerspexObject instance, object value)
        {
            if (Setter == null)
            {
                throw new ArgumentException($"The property {Name} is readonly.");
            }

            Setter((TOwner)instance, (TValue)value);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Gets a weak observable for a <see cref="PerspexProperty"/>.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="property">The property.</param>
        /// <returns>An observable.</returns>
        public static IObservable <object> GetWeakObservable(this IPerspexObject o, PerspexProperty property)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(property != null);

            return(new WeakPropertyChangedObservable(
                       new WeakReference <IPerspexObject>(o),
                       property,
                       GetDescription(o, property)));
        }
Exemplo n.º 12
0
        /// <summary>
        /// Animates a <see cref="PerspexProperty"/>.
        /// </summary>
        /// <typeparam name="T">The property type.</typeparam>
        /// <param name="target">The target object.</param>
        /// <param name="property">The target property.</param>
        /// <param name="start">The value of the property at the start of the animation.</param>
        /// <param name="finish">The value of the property at the end of the animation.</param>
        /// <param name="easing">The easing function to use.</param>
        /// <param name="duration">The duration of the animation.</param>
        /// <returns>An <see cref="Animation"/> that can be used to track or stop the animation.</returns>
        public static Animation <T> Property <T>(
            IPerspexObject target,
            PerspexProperty <T> property,
            T start,
            T finish,
            IEasing <T> easing,
            TimeSpan duration)
        {
            var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish));

            return(new Animation <T>(o, target.Bind(property, o, BindingPriority.Animation)));
        }
Exemplo n.º 13
0
        /// <inheritdoc/>
        public InstancedBinding Initiate(
            IPerspexObject target,
            PerspexProperty targetProperty,
            object anchor = null)
        {
            Contract.Requires <ArgumentNullException>(target != null);

            var pathInfo = ParsePath(Path);

            ValidateState(pathInfo);

            ExpressionObserver observer;

            if (pathInfo.ElementName != null || ElementName != null)
            {
                observer = CreateElementObserver(
                    (target as IControl) ?? (anchor as IControl),
                    pathInfo.ElementName ?? ElementName,
                    pathInfo.Path);
            }
            else if (Source != null)
            {
                observer = CreateSourceObserver(Source, pathInfo.Path);
            }
            else if (RelativeSource == null || RelativeSource.Mode == RelativeSourceMode.DataContext)
            {
                observer = CreateDataContexObserver(
                    target,
                    pathInfo.Path,
                    targetProperty == Control.DataContextProperty,
                    anchor);
            }
            else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
            {
                observer = CreateTemplatedParentObserver(target, pathInfo.Path);
            }
            else
            {
                throw new NotSupportedException();
            }

            var subject = new ExpressionSubject(
                observer,
                targetProperty?.PropertyType ?? typeof(object),
                Converter ?? DefaultValueConverter.Instance,
                ConverterParameter,
                FallbackValue,
                Priority);

            return(new InstancedBinding(subject, Mode, Priority));
        }
Exemplo n.º 14
0
        /// <summary>
        /// Creates a subject that can be used to get and set the value of the binding.
        /// </summary>
        /// <param name="target">The target instance.</param>
        /// <param name="targetProperty">The target property.</param>
        /// <returns>An <see cref="ISubject{Object}"/>.</returns>
        public ISubject<object> CreateSubject(IPerspexObject target, PerspexProperty targetProperty)
        {
            if (Converter == null)
            {
                throw new NotSupportedException("MultiBinding without Converter not currently supported.");
            }

            var targetType = targetProperty?.PropertyType ?? typeof(object);
            var result = new BehaviorSubject<object>(PerspexProperty.UnsetValue);
            var children = Bindings.Select(x => x.CreateSubject(target, null));
            var input = children.CombineLatest().Select(x => ConvertValue(x, targetType));
            input.Subscribe(result);
            return result;
        }
Exemplo n.º 15
0
        /// <summary>
        /// Called when the <see cref="DataContext"/> property begins and ends being notified.
        /// </summary>
        /// <param name="o">The object on which the DataContext is changing.</param>
        /// <param name="notifying">Whether the notifcation is beginning or ending.</param>
        private static void DataContextNotifying(IPerspexObject o, bool notifying)
        {
            var control = o as Control;

            if (control != null)
            {
                control.IsDataContextChanging = notifying;

                if (!notifying)
                {
                    control.OnDataContextChanged();
                }
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Creates a subject that can be used to get and set the value of the binding.
        /// </summary>
        /// <param name="target">The target instance.</param>
        /// <param name="targetProperty">The target property.</param>
        /// <returns>An <see cref="ISubject{Object}"/>.</returns>
        public ISubject <object> CreateSubject(IPerspexObject target, PerspexProperty targetProperty)
        {
            if (Converter == null)
            {
                throw new NotSupportedException("MultiBinding without Converter not currently supported.");
            }

            var targetType = targetProperty?.PropertyType ?? typeof(object);
            var result     = new BehaviorSubject <object>(PerspexProperty.UnsetValue);
            var children   = Bindings.Select(x => x.CreateSubject(target, null));
            var input      = children.CombineLatest().Select(x => ConvertValue(x, targetType));

            input.Subscribe(result);
            return(result);
        }
Exemplo n.º 17
0
        /// <summary>
        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IPerspexObject"/>.
        /// </summary>
        /// <param name="target">The target object.</param>
        /// <param name="property">The property to bind.</param>
        /// <param name="binding">The instanced binding.</param>
        /// <param name="anchor">
        /// An optional anchor from which to locate required context. When binding to objects that
        /// are not in the logical tree, certain types of binding need an anchor into the tree in
        /// order to locate named controls or resources. The <paramref name="anchor"/> parameter
        /// can be used to provice this context.
        /// </param>
        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
        public static IDisposable Apply(
            IPerspexObject target,
            PerspexProperty property,
            InstancedBinding binding,
            object anchor)
        {
            Contract.Requires <ArgumentNullException>(target != null);
            Contract.Requires <ArgumentNullException>(property != null);
            Contract.Requires <ArgumentNullException>(binding != null);

            var mode = binding.Mode;

            if (mode == BindingMode.Default)
            {
                mode = property.GetMetadata(target.GetType()).DefaultBindingMode;
            }

            switch (mode)
            {
            case BindingMode.Default:
            case BindingMode.OneWay:
                return(target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority));

            case BindingMode.TwoWay:
                return(new CompositeDisposable(
                           target.Bind(property, binding.Subject, binding.Priority),
                           target.GetObservable(property).Subscribe(binding.Subject)));

            case BindingMode.OneTime:
                var source = binding.Subject ?? binding.Observable;

                if (source != null)
                {
                    return(source.Take(1).Subscribe(x => target.SetValue(property, x, binding.Priority)));
                }
                else
                {
                    target.SetValue(property, binding.Value, binding.Priority);
                    return(Disposable.Empty);
                }

            case BindingMode.OneWayToSource:
                return(target.GetObservable(property).Subscribe(binding.Subject));

            default:
                throw new ArgumentException("Invalid binding mode.");
            }
        }
Exemplo n.º 18
0
        private ExpressionObserver CreateTemplatedParentObserver(
            IPerspexObject target,
            string path)
        {
            Contract.Requires <ArgumentNullException>(target != null);

            var update = target.GetObservable(Control.TemplatedParentProperty)
                         .Skip(1)
                         .Select(_ => Unit.Default);

            var result = new ExpressionObserver(
                () => target.GetValue(Control.TemplatedParentProperty),
                path,
                update);

            return(result);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Creates a subject that can be used to get and set the value of the binding.
        /// </summary>
        /// <param name="target">The target instance.</param>
        /// <param name="targetProperty">The target property. May be null.</param>
        /// <returns>An <see cref="ISubject{Object}"/>.</returns>
        public ISubject<object> CreateSubject(
            IPerspexObject target, 
            PerspexProperty targetProperty)
        {
            Contract.Requires<ArgumentNullException>(target != null);

            var pathInfo = ParsePath(Path);
            ValidateState(pathInfo);

            ExpressionObserver observer;

            if (pathInfo.ElementName != null || ElementName != null)
            {
                observer = CreateElementObserver(
                    (IControl)target, 
                    pathInfo.ElementName ?? ElementName, 
                    pathInfo.Path);
            }
            else if (Source != null)
            {
                observer = CreateSourceObserver(Source, pathInfo.Path);
            }
            else if (RelativeSource == null || RelativeSource.Mode == RelativeSourceMode.DataContext)
            {
                observer = CreateDataContexObserver(
                    target, 
                    pathInfo.Path,
                    targetProperty == Control.DataContextProperty);
            }
            else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
            {
                observer = CreateTemplatedParentObserver(target, pathInfo.Path);
            }
            else
            {
                throw new NotSupportedException();
            }

            return new ExpressionSubject(
                observer,
                targetProperty?.PropertyType ?? typeof(object),
                Converter ?? DefaultValueConverter.Instance,
                ConverterParameter,
                FallbackValue);
        }
Exemplo n.º 20
0
        /// <summary>
        /// Gets a subject for a <see cref="PerspexProperty"/>.
        /// </summary>
        /// <typeparam name="T">The property type.</typeparam>
        /// <param name="o">The object.</param>
        /// <param name="property">The property.</param>
        /// <param name="priority">
        /// The priority with which binding values are written to the object.
        /// </param>
        /// <returns>
        /// An <see cref="ISubject{T}"/> which can be used for two-way binding to/from the
        /// property.
        /// </returns>
        public static ISubject <T> GetSubject <T>(
            this IPerspexObject o,
            PerspexProperty <T> property,
            BindingPriority priority = BindingPriority.LocalValue)
        {
            // TODO: Subject.Create<T> is not yet in stable Rx : once it is, remove the
            // AnonymousSubject classes from this file and use Subject.Create<T>.
            var output = new Subject <T>();
            var result = new AnonymousSubject <T>(
                Observer.Create <T>(
                    x => output.OnNext(x),
                    e => output.OnError(e),
                    () => output.OnCompleted()),
                o.GetObservable(property));

            o.Bind(property, output, priority);
            return(result);
        }
Exemplo n.º 21
0
        /// <summary>
        /// Binds a property on an <see cref="IPerspexObject"/> to an <see cref="IBinding"/>.
        /// </summary>
        /// <param name="target">The object.</param>
        /// <param name="property">The property to bind.</param>
        /// <param name="binding">The binding.</param>
        /// <param name="anchor">
        /// An optional anchor from which to locate required context. When binding to objects that
        /// are not in the logical tree, certain types of binding need an anchor into the tree in
        /// order to locate named controls or resources. The <paramref name="anchor"/> parameter
        /// can be used to provice this context.
        /// </param>
        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
        public static IDisposable Bind(
            this IPerspexObject target,
            PerspexProperty property,
            IBinding binding,
            object anchor = null)
        {
            Contract.Requires <ArgumentNullException>(target != null);
            Contract.Requires <ArgumentNullException>(property != null);
            Contract.Requires <ArgumentNullException>(binding != null);

            var result = binding.Initiate(target, property, anchor);

            if (result != null)
            {
                return(BindingOperations.Apply(target, property, result, anchor));
            }
            else
            {
                return(Disposable.Empty);
            }
        }
Exemplo n.º 22
0
        private ExpressionObserver CreateDataContexObserver(
            IPerspexObject target,
            string path,
            bool targetIsDataContext,
            object anchor)
        {
            Contract.Requires <ArgumentNullException>(target != null);

            if (!(target is IControl))
            {
                target = anchor as IControl;

                if (target == null)
                {
                    throw new InvalidOperationException("Cannot find a DataContext to bind to.");
                }
            }

            if (!targetIsDataContext)
            {
                var update = target.GetObservable(Control.DataContextProperty)
                             .Skip(1)
                             .Select(_ => Unit.Default);
                var result = new ExpressionObserver(
                    () => target.GetValue(Control.DataContextProperty),
                    path,
                    update);

                return(result);
            }
            else
            {
                return(new ExpressionObserver(
                           target.GetObservable(Visual.VisualParentProperty)
                           .OfType <IPerspexObject>()
                           .Select(x => x.GetObservable(Control.DataContextProperty))
                           .Switch(),
                           path));
            }
        }
Exemplo n.º 23
0
        /// <summary>
        /// Binds a property on an <see cref="IPerspexObject"/> to an <see cref="IBinding"/>.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="property">The property to bind.</param>
        /// <param name="binding">The binding.</param>
        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
        public static IDisposable Bind(
            this IPerspexObject o,
            PerspexProperty property,
            IBinding binding)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(property != null);
            Contract.Requires <ArgumentNullException>(binding != null);

            var mode = binding.Mode;

            if (mode == BindingMode.Default)
            {
                mode = property.GetMetadata(o.GetType()).DefaultBindingMode;
            }

            return(o.Bind(
                       property,
                       binding.CreateSubject(o, property),
                       mode,
                       binding.Priority));
        }
Exemplo n.º 24
0
 /// <inheritdoc/>
 object IDirectPropertyAccessor.GetValue(IPerspexObject instance)
 {
     return(Getter((TOwner)instance));
 }
Exemplo n.º 25
0
        private ExpressionObserver CreateTemplatedParentObserver(
            IPerspexObject target,
            string path)
        {
            Contract.Requires<ArgumentNullException>(target != null);

            var update = target.GetObservable(Control.TemplatedParentProperty)
                .Skip(1)
                .Select(_ => Unit.Default);

            var result = new ExpressionObserver(
                () => target.GetValue(Control.TemplatedParentProperty),
                path,
                update);

            return result;
        }
Exemplo n.º 26
0
        private ExpressionObserver CreateDataContexObserver(
            IPerspexObject target,
            string path,
            bool targetIsDataContext)
        {
            Contract.Requires<ArgumentNullException>(target != null);

            if (!targetIsDataContext)
            {
                var update = target.GetObservable(Control.DataContextProperty)
                    .Skip(1)
                    .Select(_ => Unit.Default);
                var result = new ExpressionObserver(
                    () => target.GetValue(Control.DataContextProperty),
                    path,
                    update);

                return result;
            }
            else
            {
                return new ExpressionObserver(
                    target.GetObservable(Visual.VisualParentProperty)
                          .OfType<IPerspexObject>()
                          .Select(x => x.GetObservable(Control.DataContextProperty))
                          .Switch(),
                    path);
            }
        }
Exemplo n.º 27
0
        /// <summary>
        /// Applies a binding subject to a property on an instance.
        /// </summary>
        /// <param name="target">The target instance.</param>
        /// <param name="property">The target property.</param>
        /// <param name="subject">The binding subject.</param>
        internal void Bind(IPerspexObject target, PerspexProperty property, ISubject<object> subject)
        {
            var mode = Mode == BindingMode.Default ?
                property.DefaultBindingMode : Mode;

            switch (mode)
            {
                case BindingMode.Default:
                case BindingMode.OneWay:
                    target.Bind(property, subject, Priority);
                    break;
                case BindingMode.TwoWay:
                    throw new NotSupportedException("TwoWay MultiBinding not currently supported.");
                case BindingMode.OneTime:
                    target.GetObservable(Control.DataContextProperty).Subscribe(dataContext =>
                    {
                        subject.Take(1).Subscribe(x => target.SetValue(property, x, Priority));
                    });                    
                    break;
                case BindingMode.OneWayToSource:
                    target.GetObservable(property).Subscribe(subject);
                    break;
            }
        }
Exemplo n.º 28
0
 /// <summary>
 /// Gets a description of a property that van be used in observables.
 /// </summary>
 /// <param name="o">The object.</param>
 /// <param name="property">The property</param>
 /// <returns>The description.</returns>
 private static string GetDescription(IPerspexObject o, PerspexProperty property)
 {
     return($"{o.GetType().Name}.{property.Name}");
 }