/// <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.");
            }
        }
 protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification notification)
 {
     Notifications.Add(notification);
 }
Example #3
0
 /// <summary>
 /// Compares a value to an instance of <see cref="BindingNotification"/> for equality.
 /// </summary>
 /// <param name="other">The value to compare.</param>
 /// <returns>true if the two instances are equal; otherwise false.</returns>
 public bool Equals(BindingNotification other)
 {
     return(this == other);
 }
Example #4
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.");
            }
        }
Example #5
0
 /// <summary>
 /// Called when a priority level encounters an error.
 /// </summary>
 /// <param name="level">The priority level of the changed entry.</param>
 /// <param name="error">The binding error.</param>
 public void LevelError(PriorityLevel level, BindingNotification error)
 {
     Logger.Log(
         LogEventLevel.Error,
         LogArea.Binding,
         _owner,
         "Error in binding to {Target}.{Property}: {Message}",
         _owner,
         Property,
         error.Error.Message);
 }
 protected override void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
 {
     Notifications.Add(notification);
 }
 /// <summary>
 /// Compares a value to an instance of <see cref="BindingNotification"/> for equality.
 /// </summary>
 /// <param name="other">The value to compare.</param>
 /// <returns>true if the two instances are equal; otherwise false.</returns>
 public bool Equals(BindingNotification other)
 {
     return this == other;
 }
Example #8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PropertyError"/> class.
 /// </summary>
 /// <param name="error">The error to report.</param>
 public PropertyError(BindingNotification error)
 {
     _error = error;
 }
Example #9
0
 /// <summary>
 /// Invoked when an entry in <see cref="Bindings"/> encounters a recoverable error.
 /// </summary>
 /// <param name="entry">The entry that completed.</param>
 /// <param name="error">The error.</param>
 public void Error(PriorityBindingEntry entry, BindingNotification error)
 {
     _owner.LevelError(this, error);
 }