/// <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); }
/// <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); }
/// <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."); } }
/// <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; }
/// <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; }
/// <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); }