From 433c70a6c826af2bf65fad745a4ff160502deab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sun, 9 Jun 2019 21:26:08 +0100 Subject: [PATCH 1/2] Added BindingOperations.DoNothing. Added tests. --- src/Avalonia.Base/Data/BindingOperations.cs | 2 + .../Data/Core/BindingExpression.cs | 31 ++++++++ .../Avalonia.Markup/Data/MultiBinding.cs | 7 +- .../Converters/ValueConverterTests.cs | 71 +++++++++++++++++++ .../Data/BindingTests.cs | 20 +++--- 5 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index 15de6c4d0d..44b47329ac 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/src/Avalonia.Base/Data/BindingOperations.cs @@ -10,6 +10,8 @@ namespace Avalonia.Data { public static class BindingOperations { + public static readonly object DoNothing = new object(); + /// /// Applies an a property on an . /// diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index f1717bde3b..7f8396cdfa 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -114,6 +114,11 @@ namespace Avalonia.Data.Core /// public void OnNext(object value) { + if (value == BindingOperations.DoNothing) + { + return; + } + using (_inner.Subscribe(_ => { })) { var type = _inner.ResultType; @@ -126,6 +131,11 @@ namespace Avalonia.Data.Core ConverterParameter, CultureInfo.CurrentCulture); + if (converted == BindingOperations.DoNothing) + { + return; + } + if (converted == AvaloniaProperty.UnsetValue) { converted = TypeUtilities.Default(type); @@ -186,6 +196,11 @@ namespace Avalonia.Data.Core /// private object ConvertValue(object value) { + if (value == BindingOperations.DoNothing) + { + return value; + } + var notification = value as BindingNotification; if (notification == null) @@ -196,6 +211,11 @@ namespace Avalonia.Data.Core ConverterParameter, CultureInfo.CurrentCulture); + if (converted == BindingOperations.DoNothing) + { + return converted; + } + notification = converted as BindingNotification; if (notification?.ErrorType == BindingErrorType.None) @@ -327,7 +347,18 @@ namespace Avalonia.Data.Core public void OnNext(object value) { + if (value == BindingOperations.DoNothing) + { + return; + } + var converted = _owner.ConvertValue(value); + + if (converted == BindingOperations.DoNothing) + { + return; + } + _owner._value = new WeakReference(converted); _owner.PublishNext(converted); } diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index eb2a8df2eb..d1e3765dfa 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -92,7 +92,12 @@ namespace Avalonia.Data var culture = CultureInfo.CurrentCulture; var converted = Converter.Convert(values, targetType, ConverterParameter, culture); - if (converted == AvaloniaProperty.UnsetValue && FallbackValue != null) + if (converted == BindingOperations.DoNothing) + { + return converted; + } + + if (converted == AvaloniaProperty.UnsetValue) { converted = FallbackValue; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs new file mode 100644 index 0000000000..6f2c4363e2 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Converters +{ + public class ValueConverterTests + { + [Fact] + public void ValueConverter_Special_Values_Work() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.ApplyTemplate(); + + window.DataContext = 2; + Assert.Equal("foo", textBlock.Text); + + window.DataContext = -3; + Assert.Equal("foo", textBlock.Text); + + window.DataContext = 0; + Assert.Equal("bar", textBlock.Text); + } + } + } + + public class TestConverter : IValueConverter + { + public static readonly TestConverter Instance = new TestConverter(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is int i) + { + if (i > 0) + { + return "foo"; + } + + if (i == 0) + { + return AvaloniaProperty.UnsetValue; + } + + return BindingOperations.DoNothing; + } + + return "(default)"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index 0a00b9d39d..9813c1f222 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -1,20 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Data; -using Avalonia.Markup.Data; -using Moq; -using Xunit; -using System.ComponentModel; -using System.Runtime.CompilerServices; using Avalonia.UnitTests; -using Avalonia.Data.Converters; -using Avalonia.Data.Core; +using Xunit; namespace Avalonia.Markup.Xaml.UnitTests.Data { @@ -34,10 +24,16 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data var window = (Window)loader.Load(xaml); var textBlock = window.FindControl("textBlock"); - window.DataContext = "foo"; window.ApplyTemplate(); + window.DataContext = "foo"; Assert.Equal("foo", textBlock.Text); + + window.DataContext = BindingOperations.DoNothing; + Assert.Equal("foo", textBlock.Text); + + window.DataContext = "bar"; + Assert.Equal("bar", textBlock.Text); } } } From 5adf7c3a4cda4f598dd5ab93e04b7bb8a5f7bb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Wed, 26 Jun 2019 17:48:03 +0100 Subject: [PATCH 2/2] Moved test to separate method. --- .../Data/BindingTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index 9813c1f222..e412657711 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -24,6 +24,27 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data var window = (Window)loader.Load(xaml); var textBlock = window.FindControl("textBlock"); + window.DataContext = "foo"; + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } + + [Fact] + public void Binding_To_DoNothing_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + window.ApplyTemplate(); window.DataContext = "foo";