From 8b9f25ff2462506414e0ee7dc69ca5b5b218f641 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 18 Aug 2016 19:26:02 +0200 Subject: [PATCH] Fix OneTime bindings. OneTime bindings were failing in BindingTest because the initial binding error was being counted as the single value to transfer. Don't do this with OneTime bindings - only transfer valid values. --- src/Avalonia.Base/Data/BindingNotification.cs | 15 ++++++ src/Avalonia.Base/Data/BindingOperations.cs | 5 +- .../AvaloniaObjectTests_Binding.cs | 49 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs index d61e9ad3b2..c9e6e3b804 100644 --- a/src/Avalonia.Base/Data/BindingNotification.cs +++ b/src/Avalonia.Base/Data/BindingNotification.cs @@ -166,6 +166,21 @@ namespace Avalonia.Data return !(a == b); } + /// + /// Gets a value from an object that may be a . + /// + /// The object. + /// The value. + /// + /// If is a then returns the binding + /// notification's . If not, returns the object unchanged. + /// + public static object ExtractValue(object o) + { + var notification = o as BindingNotification; + return notification != null ? notification.Value : o; + } + /// /// Compares an object to an instance of for equality. /// diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index 9899eb633c..eb7c449bec 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/src/Avalonia.Base/Data/BindingOperations.cs @@ -54,7 +54,10 @@ namespace Avalonia.Data if (source != null) { - return source.Take(1).Subscribe(x => target.SetValue(property, x, binding.Priority)); + return source + .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue) + .Take(1) + .Subscribe(x => target.SetValue(property, x, binding.Priority)); } else { diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 5c3459e3db..1757550d4a 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -54,6 +54,36 @@ namespace Avalonia.Base.UnitTests Assert.False(target.IsSet(Class1.QuxProperty)); } + [Fact] + public void OneTime_Binding_Ignores_UnsetValue() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.QuxProperty, new TestOneTimeBinding(source)); + + source.OnNext(AvaloniaProperty.UnsetValue); + Assert.Equal(5.6, target.GetValue(Class1.QuxProperty)); + + source.OnNext(6.7); + Assert.Equal(6.7, target.GetValue(Class1.QuxProperty)); + } + + [Fact] + public void OneTime_Binding_Ignores_Binding_Errors() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.QuxProperty, new TestOneTimeBinding(source)); + + source.OnNext(new BindingNotification(new Exception(), BindingErrorType.Error)); + Assert.Equal(5.6, target.GetValue(Class1.QuxProperty)); + + source.OnNext(6.7); + Assert.Equal(6.7, target.GetValue(Class1.QuxProperty)); + } + [Fact] public void Bind_Throws_Exception_For_Unregistered_Property() { @@ -352,5 +382,24 @@ namespace Avalonia.Base.UnitTests public static readonly StyledProperty BarProperty = AvaloniaProperty.Register("Bar", "bardefault"); } + + private class TestOneTimeBinding : IBinding + { + private IObservable _source; + + public TestOneTimeBinding(IObservable source) + { + _source = source; + } + + public InstancedBinding Initiate( + IAvaloniaObject target, + AvaloniaProperty targetProperty, + object anchor = null, + bool enableDataValidation = false) + { + return new InstancedBinding(_source, BindingMode.OneTime); + } + } } }