예제 #1
0
        /// <inheritdoc/>
        public InstancedBinding Initiate(
            IAvaloniaObject target,
            AvaloniaProperty targetProperty,
            object anchor             = null,
            bool enableDataValidation = false)
        {
            if (Converter == null)
            {
                throw new NotSupportedException("MultiBinding without Converter not currently supported.");
            }

            var targetType = targetProperty?.PropertyType ?? typeof(object);
            var children   = Bindings.Select(x => x.Initiate(target, null));
            var input      = children.Select(x => x.Subject).CombineLatest().Select(x => ConvertValue(x, targetType));
            var mode       = Mode == BindingMode.Default ?
                             targetProperty.GetMetadata(target.GetType()).DefaultBindingMode : Mode;

            switch (mode)
            {
            case BindingMode.OneTime:
                return(InstancedBinding.OneTime(input, Priority));

            case BindingMode.OneWay:
                return(InstancedBinding.OneWay(input, Priority));

            default:
                throw new NotSupportedException(
                          "MultiBinding currently only supports OneTime and OneWay BindingMode.");
            }
        }
        public InstancedBinding Initiate(
            IAvaloniaObject target,
            AvaloniaProperty targetProperty,
            object anchor             = null,
            bool enableDataValidation = false)
        {
            var mode = Mode == BindingMode.Default ?
                       targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
                       Mode;

            switch (mode)
            {
            case BindingMode.OneTime:
                return(InstancedBinding.OneTime(Source.GetObservable(Property)));

            case BindingMode.OneWay:
                return(InstancedBinding.OneWay(Source.GetObservable(Property)));

            case BindingMode.OneWayToSource:
                return(InstancedBinding.OneWayToSource(Source.GetSubject(Property)));

            case BindingMode.TwoWay:
                return(InstancedBinding.TwoWay(Source.GetSubject(Property)));

            default:
                throw new NotSupportedException("Unsupported BindingMode.");
            }
        }
        /// <summary>
        /// Finds a registered property on an object by name.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="name">The property name.</param>
        /// <returns>
        /// The registered property or null if no matching property found.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// The property name contains a '.'.
        /// </exception>
        public AvaloniaProperty?FindRegistered(IAvaloniaObject o, string name)
        {
            _ = o ?? throw new ArgumentNullException(nameof(o));
            _ = name ?? throw new ArgumentNullException(nameof(name));

            return(FindRegistered(o.GetType(), name));
        }
예제 #4
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(IAvaloniaObject target, AvaloniaProperty 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;
            }
        }
        /// <summary>
        /// Binds a property on an <see cref="IAvaloniaObject"/> 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 IAvaloniaObject target,
            AvaloniaProperty property,
            IBinding binding,
            object anchor = null)
        {
            Contract.Requires <ArgumentNullException>(target != null);
            Contract.Requires <ArgumentNullException>(property != null);
            Contract.Requires <ArgumentNullException>(binding != null);

            var metadata = property.GetMetadata(target.GetType()) as IDirectPropertyMetadata;

            var result = binding.Initiate(
                target,
                property,
                anchor,
                metadata?.EnableDataValidation ?? false);

            if (result != null)
            {
                return(BindingOperations.Apply(target, property, result, anchor));
            }
            else
            {
                return(Disposable.Empty);
            }
        }
예제 #6
0
        /// <summary>
        /// Finds a registered property on an object by name.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="name">The property name.</param>
        /// <returns>
        /// The registered property or null if no matching property found.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// The property name contains a '.'.
        /// </exception>
        public AvaloniaProperty FindRegistered(IAvaloniaObject o, string name)
        {
            Contract.Requires <ArgumentNullException>(o != null);
            Contract.Requires <ArgumentNullException>(name != null);

            return(FindRegistered(o.GetType(), name));
        }
예제 #7
0
 private static void SetContentProperty(IAvaloniaObject targetLocation, object?view)
 {
     if (view != null && targetLocation != null)
     {
         Type?type = targetLocation.GetType();
         type.GetProperty("Content")?.SetValue(targetLocation, view);
     }
 }
예제 #8
0
        /// <summary>
        /// Finds a direct property as registered on an object.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="property">The direct property.</param>
        /// <returns>
        /// The registered property or null if no matching property found.
        /// </returns>
        public DirectPropertyBase <T> FindRegisteredDirect <T>(
            IAvaloniaObject o,
            DirectPropertyBase <T> property)
        {
            if (property.Owner == o.GetType())
            {
                return(property);
            }

            foreach (var p in GetRegisteredDirect(o.GetType()))
            {
                if (p == property)
                {
                    return((DirectPropertyBase <T>)p);
                }
            }

            return(null);
        }
        /// <summary>
        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>.
        /// </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 provide this context.
        /// </param>
        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
        public static IDisposable Apply(
            IAvaloniaObject target,
            AvaloniaProperty 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
                           .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
                           .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(Observable.CombineLatest(
                           binding.Observable,
                           target.GetObservable(property),
                           (_, v) => v)
                       .Subscribe(x => binding.Subject.OnNext(x)));

            default:
                throw new ArgumentException("Invalid binding mode.");
            }
        }
예제 #10
0
        public TValue CoerceValue(IAvaloniaObject instance, TValue baseValue)
        {
            var metadata = GetMetadata(instance.GetType());

            if (metadata.CoerceValue != null)
            {
                return(metadata.CoerceValue.Invoke(instance, baseValue));
            }

            return(baseValue);
        }
예제 #11
0
        public InstancedBinding Initiate(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor = null)
        {
            var mode = Mode == BindingMode.Default ?
                       targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
                       Mode;

            if (mode == BindingMode.TwoWay)
            {
                return(new InstancedBinding(Source.GetSubject(Property), mode));
            }
            else
            {
                return(new InstancedBinding(Source.GetObservable(Property), mode));
            }
        }
        /// <summary>
        /// Finds a direct property as registered on an object.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <param name="property">The direct property.</param>
        /// <returns>
        /// The registered property or null if no matching property found.
        /// </returns>
        public DirectPropertyBase <T>?FindRegisteredDirect <T>(
            IAvaloniaObject o,
            DirectPropertyBase <T> property)
        {
            if (property.Owner == o.GetType())
            {
                return(property);
            }

            var registeredDirect      = GetRegisteredDirect(o.GetType());
            var registeredDirectCount = registeredDirect.Count;

            for (var i = 0; i < registeredDirectCount; i++)
            {
                var p = registeredDirect[i];

                if (p == property)
                {
                    return((DirectPropertyBase <T>)p);
                }
            }

            return(null);
        }
예제 #13
0
        public PriorityValue(
            IAvaloniaObject owner,
            StyledPropertyBase <T> property,
            IValueSink sink)
        {
            _owner   = owner;
            Property = property;
            _sink    = sink;

            if (property.HasCoercion)
            {
                var metadata = property.GetMetadata(owner.GetType());
                _coerceValue = metadata.CoerceValue;
            }
        }
예제 #14
0
        public InstancedBinding Initiate(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor = null)
        {
            var mode = Mode == BindingMode.Default ?
                targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
                Mode;

            if (mode == BindingMode.TwoWay)
            {
                return new InstancedBinding(Source.GetSubject(Property), mode);
            }
            else
            {
                return new InstancedBinding(Source.GetObservable(Property), mode);
            }
        }
예제 #15
0
        /// <summary>
        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>.
        /// </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(
            IAvaloniaObject target,
            AvaloniaProperty 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
                            .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
                            .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.");
            }
        }
예제 #16
0
        /// <inheritdoc/>
        public InstancedBinding?Initiate(
            IAvaloniaObject target,
            AvaloniaProperty?targetProperty,
            object?anchor             = null,
            bool enableDataValidation = false)
        {
            var targetType = targetProperty?.PropertyType ?? typeof(object);
            var converter  = Converter;

            // We only respect `StringFormat` if the type of the property we're assigning to will
            // accept a string. Note that this is slightly different to WPF in that WPF only applies
            // `StringFormat` for target type `string` (not `object`).
            if (!string.IsNullOrWhiteSpace(StringFormat) &&
                (targetType == typeof(string) || targetType == typeof(object)))
            {
                converter = new StringFormatMultiValueConverter(StringFormat !, converter);
            }

            var children = Bindings.Select(x => x.Initiate(target, null));

            var input = children.Select(x => x?.Observable !)
                        .Where(x => x is not null)
                        .CombineLatest()
                        .Select(x => ConvertValue(x, targetType, converter))
                        .Where(x => x != BindingOperations.DoNothing);

            var mode = Mode == BindingMode.Default ?
                       targetProperty?.GetMetadata(target.GetType()).DefaultBindingMode : Mode;

            switch (mode)
            {
            case BindingMode.OneTime:
                return(InstancedBinding.OneTime(input, Priority));

            case BindingMode.OneWay:
                return(InstancedBinding.OneWay(input, Priority));

            default:
                throw new NotSupportedException(
                          "MultiBinding currently only supports OneTime and OneWay BindingMode.");
            }
        }
예제 #17
0
        /// <summary>
        /// Method that will create the region, by calling the right <see cref="IRegionAdapter"/>.
        /// </summary>
        /// <param name="targetElement">The target element that will host the <see cref="IRegion"/>.</param>
        /// <param name="regionName">Name of the region.</param>
        /// <returns>The created <see cref="IRegion"/></returns>
        protected virtual IRegion CreateRegion(IAvaloniaObject targetElement, string regionName)
        {
            if (targetElement == null)
            {
                throw new ArgumentNullException(nameof(targetElement));
            }

            try
            {
                // Build the region
                IRegionAdapter regionAdapter = this.regionAdapterMappings.GetMapping(targetElement.GetType());
                IRegion        region        = regionAdapter.Initialize(targetElement, regionName);

                return(region);
            }
            catch (Exception ex)
            {
                throw new RegionCreationException(string.Format(CultureInfo.CurrentCulture, Resources.RegionCreationException, regionName, ex), ex);
            }
        }
예제 #18
0
        private void UpdateValue(BindingValue <T> value)
        {
            if (value.HasValue && Property.ValidateValue?.Invoke(value.Value) == false)
            {
                value = Property.GetDefaultValue(_owner.GetType());
            }

            if (value.Type == BindingValueType.DoNothing)
            {
                return;
            }

            var old = Value;

            if (value.Type != BindingValueType.DataValidationError)
            {
                Value = value.ToOptional();
            }

            _sink.ValueChanged(Property, Priority, old, value);
        }
예제 #19
0
        protected TreeNode(IAvaloniaObject avaloniaObject, TreeNode?parent, string?customName = null)
        {
            _classes = string.Empty;
            Parent   = parent;
            var visual = avaloniaObject;

            Type       = customName ?? avaloniaObject.GetType().Name;
            Visual     = visual !;
            FontWeight = IsRoot ? FontWeight.Bold : FontWeight.Normal;

            if (visual is IControl control)
            {
                ElementName = control.Name;

                var removed = Observable.FromEventPattern <LogicalTreeAttachmentEventArgs>(
                    x => control.DetachedFromLogicalTree += x,
                    x => control.DetachedFromLogicalTree -= x);
                var classesChanged = Observable.FromEventPattern <
                    NotifyCollectionChangedEventHandler,
                    NotifyCollectionChangedEventArgs>(
                    x => control.Classes.CollectionChanged += x,
                    x => control.Classes.CollectionChanged -= x)
                                     .TakeUntil(removed);

                _classesSubscription = classesChanged.Select(_ => Unit.Default)
                                       .StartWith(Unit.Default)
                                       .Subscribe(_ =>
                {
                    if (control.Classes.Count > 0)
                    {
                        Classes = "(" + string.Join(" ", control.Classes) + ")";
                    }
                    else
                    {
                        Classes = string.Empty;
                    }
                });
            }
        }
예제 #20
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(IAvaloniaObject target, AvaloniaProperty 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;
            }
        }
예제 #21
0
        /// <summary>
        /// Gets all <see cref="AvaloniaProperty"/>s registered on a object.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
        public IReadOnlyList <AvaloniaProperty> GetRegistered(IAvaloniaObject o)
        {
            Contract.Requires <ArgumentNullException>(o != null);

            return(GetRegistered(o.GetType()));
        }
        /// <summary>
        /// Gets all <see cref="AvaloniaProperty"/>s registered on a object.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
        public IReadOnlyList <AvaloniaProperty> GetRegistered(IAvaloniaObject o)
        {
            _ = o ?? throw new ArgumentNullException(nameof(o));

            return(GetRegistered(o.GetType()));
        }
예제 #23
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(IAvaloniaObject o, AvaloniaProperty property)
 {
     return($"{o.GetType().Name}.{property.Name}");
 }
 /// <summary>
 /// Gets a direct property as registered on an object.
 /// </summary>
 /// <param name="o">The object.</param>
 /// <param name="property">The direct property.</param>
 /// <returns>
 /// The registered.
 /// </returns>
 public DirectPropertyBase <T> GetRegisteredDirect <T>(
     IAvaloniaObject o,
     DirectPropertyBase <T> property)
 {
     return(FindRegisteredDirect(o, property) ??
            throw new ArgumentException($"Property '{property.Name} not registered on '{o.GetType()}"));
 }
예제 #25
0
        /// <summary>
        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>.
        /// </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 provide this context.
        /// </param>
        /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
        public static IDisposable Apply(
            IAvaloniaObject target,
            AvaloniaProperty property,
            InstancedBinding binding,
            object?anchor)
        {
            _ = target ?? throw new ArgumentNullException(nameof(target));
            _ = property ?? throw new ArgumentNullException(nameof(property));
            _ = binding ?? throw new ArgumentNullException(nameof(binding));

            var mode = binding.Mode;

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

            switch (mode)
            {
            case BindingMode.Default:
            case BindingMode.OneWay:
                if (binding.Observable is null)
                {
                    throw new InvalidOperationException("InstancedBinding does not contain an observable.");
                }
                return(target.Bind(property, binding.Observable, binding.Priority));

            case BindingMode.TwoWay:
                if (binding.Subject is null)
                {
                    throw new InvalidOperationException("InstancedBinding does not contain a subject.");
                }
                return(new TwoWayBindingDisposable(
                           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)
                {
                    // Perf: Avoid allocating closure in the outer scope.
                    var targetCopy   = target;
                    var propertyCopy = property;
                    var bindingCopy  = binding;

                    return(source
                           .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
                           .Take(1)
                           .Subscribe(x => targetCopy.SetValue(
                                          propertyCopy,
                                          BindingNotification.ExtractValue(x),
                                          bindingCopy.Priority)));
                }
                else
                {
                    target.SetValue(property, binding.Value, binding.Priority);
                    return(Disposable.Empty);
                }

            case BindingMode.OneWayToSource:
            {
                if (binding.Observable is null)
                {
                    throw new InvalidOperationException("InstancedBinding does not contain an observable.");
                }
                if (binding.Subject is null)
                {
                    throw new InvalidOperationException("InstancedBinding does not contain a subject.");
                }

                // Perf: Avoid allocating closure in the outer scope.
                var bindingCopy = binding;

                return(Observable.CombineLatest(
                           binding.Observable,
                           target.GetObservable(property),
                           (_, v) => v)
                       .Subscribe(x => bindingCopy.Subject.OnNext(x)));
            }

            default:
                throw new ArgumentException("Invalid binding mode.");
            }
        }