public void Setter_Should_Apply_Binding_To_Property() { var control = new TextBlock(); var subject = new BehaviorSubject<object>("foo"); var descriptor = new InstancedBinding(subject); var binding = Mock.Of<IBinding>(x => x.Initiate(control, TextBlock.TextProperty, null, false) == descriptor); var style = Mock.Of<IStyle>(); var setter = new Setter(TextBlock.TextProperty, binding); setter.Apply(style, control, null); Assert.Equal("foo", control.Text); }
/// <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."); } }
/// <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) .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."); } }
/// <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) { // 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, x, bindingCopy.Priority))); } else { target.SetValue(property, binding.Value, binding.Priority); return(Disposable.Empty); } case BindingMode.OneWayToSource: { // 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."); } }
private InstancedBinding Clone(InstancedBinding sourceInstance, IStyle style, IObservable<bool> activator) { InstancedBinding cloned; if (activator != null) { var description = style?.ToString(); if (sourceInstance.Subject != null) { var activated = new ActivatedSubject(activator, sourceInstance.Subject, description); cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger); } else if (sourceInstance.Observable != null) { var activated = new ActivatedObservable(activator, sourceInstance.Observable, description); cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger); } else { var activated = new ActivatedValue(activator, sourceInstance.Value, description); cloned = new InstancedBinding(activated, BindingMode.OneWay, BindingPriority.StyleTrigger); } } else { if (sourceInstance.Subject != null) { cloned = new InstancedBinding(sourceInstance.Subject, sourceInstance.Mode, BindingPriority.Style); } else if (sourceInstance.Observable != null) { cloned = new InstancedBinding(sourceInstance.Observable, sourceInstance.Mode, BindingPriority.Style); } else { cloned = new InstancedBinding(sourceInstance.Value, BindingPriority.Style); } } return cloned; }