From f3dfddc112739ac8c1602fbf9516004b803d54c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Wed, 21 Aug 2019 01:15:55 +0100 Subject: [PATCH 01/10] Fixed Binding.DoNothing for MultiBinding. --- .../Avalonia.Markup/Data/MultiBinding.cs | 12 +-- .../Converters/MultiValueConverterTests.cs | 76 +++++++++++++++++++ 2 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index 19f92149ec..29945e25c3 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -76,7 +76,12 @@ namespace Avalonia.Data } var children = Bindings.Select(x => x.Initiate(target, null)); - var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType, converter)); + + 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; @@ -97,11 +102,6 @@ namespace Avalonia.Data var culture = CultureInfo.CurrentCulture; var converted = converter.Convert(values, targetType, ConverterParameter, culture); - if (converted == BindingOperations.DoNothing) - { - return converted; - } - if (converted == AvaloniaProperty.UnsetValue) { converted = FallbackValue; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs new file mode 100644 index 0000000000..a77723afe1 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +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 MultiValueConverterTests : XamlTestBase + { + [Fact] + public void MultiValueConverter_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 = Tuple.Create(2, 2); + Assert.Equal("foo", textBlock.Text); + + window.DataContext = Tuple.Create(-3, 3); + Assert.Equal("foo", textBlock.Text); + + window.DataContext = Tuple.Create(0, 2); + Assert.Equal("bar", textBlock.Text); + } + } + } + + public class TestMultiValueConverter : IMultiValueConverter + { + public static readonly TestMultiValueConverter Instance = new TestMultiValueConverter(); + + public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + { + if (values[0] is int i && values[1] is int j) + { + var p = i * j; + + if (p > 0) + { + return "foo"; + } + + if (p == 0) + { + return AvaloniaProperty.UnsetValue; + } + + return BindingOperations.DoNothing; + } + + return "(default)"; + } + } +} From b376313acff353872ce610d39997c9c553180ed4 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 28 Aug 2019 16:28:17 +0100 Subject: [PATCH 02/10] speed up GetValue and GetDefaultValue --- src/Avalonia.Base/AvaloniaObject.cs | 49 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 0e2f0feada..2fee277f21 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -208,20 +208,9 @@ namespace Avalonia { return ((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this); } - else if (_values != null) - { - var result = Values.GetValue(property); - - if (result == AvaloniaProperty.UnsetValue) - { - result = GetDefaultValue(property); - } - - return result; - } else { - return GetDefaultValue(property); + return GetValueOrDefault(property); } } @@ -598,10 +587,44 @@ namespace Avalonia private object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValue(property); + return aobj.GetValueOrDefault(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } + /// + /// Gets the value or default value for a property. + /// + /// The property. + /// The default value. + private object GetValueOrDefault(AvaloniaProperty property) + { + var aobj = this; + if (aobj.Values != null) + { + var result = aobj.Values.GetValue(property); + if (result != AvaloniaProperty.UnsetValue) + { + return result; + } + } + if (property.Inherits) + { + while(aobj.InheritanceParent is AvaloniaObject parent) + { + aobj = parent; + if (aobj.Values != null) + { + var result = aobj.Values.GetValue(property); + if (result != AvaloniaProperty.UnsetValue) + { + return result; + } + } + } + } + return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType()); + } + /// /// Sets the value of a direct property. /// From ebccccb8e4c8a6138274eeb1e51769f93919c857 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 28 Aug 2019 17:37:01 +0100 Subject: [PATCH 03/10] add UnChecked suffix --- src/Avalonia.Base/AvaloniaObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 2fee277f21..5fa6dab29e 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -210,7 +210,7 @@ namespace Avalonia } else { - return GetValueOrDefault(property); + return GetValueOrDefaultUnChecked(property); } } @@ -596,7 +596,7 @@ namespace Avalonia /// /// The property. /// The default value. - private object GetValueOrDefault(AvaloniaProperty property) + private object GetValueOrDefaultUnChecked(AvaloniaProperty property) { var aobj = this; if (aobj.Values != null) From e8ba46160d3aad056a121b23b82f5290d8d360e6 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 28 Aug 2019 17:39:59 +0100 Subject: [PATCH 04/10] UnChecked suffix fixed --- src/Avalonia.Base/AvaloniaObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 5fa6dab29e..f8efb36562 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -587,7 +587,7 @@ namespace Avalonia private object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValueOrDefault(property); + return aobj.GetValueOrDefaultUnChecked(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } From 77d9ae1cacfee35930c0543643c34a167ffe3b87 Mon Sep 17 00:00:00 2001 From: ahopper Date: Fri, 30 Aug 2019 10:18:30 +0100 Subject: [PATCH 05/10] change UnChecked suffix to Unchecked --- src/Avalonia.Base/AvaloniaObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index f8efb36562..48a222d5b4 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -210,7 +210,7 @@ namespace Avalonia } else { - return GetValueOrDefaultUnChecked(property); + return GetValueOrDefaultUnchecked(property); } } @@ -587,7 +587,7 @@ namespace Avalonia private object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValueOrDefaultUnChecked(property); + return aobj.GetValueOrDefaultUnchecked(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } @@ -596,7 +596,7 @@ namespace Avalonia /// /// The property. /// The default value. - private object GetValueOrDefaultUnChecked(AvaloniaProperty property) + private object GetValueOrDefaultUnchecked(AvaloniaProperty property) { var aobj = this; if (aobj.Values != null) From 4d8973226d10ed188181367d5b522f0e2fd99bc7 Mon Sep 17 00:00:00 2001 From: ahopper Date: Fri, 30 Aug 2019 10:35:24 +0100 Subject: [PATCH 06/10] unbreak lazy initialization of Values --- src/Avalonia.Base/AvaloniaObject.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 48a222d5b4..035e0056e0 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -599,9 +599,9 @@ namespace Avalonia private object GetValueOrDefaultUnchecked(AvaloniaProperty property) { var aobj = this; - if (aobj.Values != null) + if (aobj._values != null) { - var result = aobj.Values.GetValue(property); + var result = aobj._values.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; @@ -612,9 +612,9 @@ namespace Avalonia while(aobj.InheritanceParent is AvaloniaObject parent) { aobj = parent; - if (aobj.Values != null) + if (aobj._values != null) { - var result = aobj.Values.GetValue(property); + var result = aobj._values.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; From 26b1320971d3e7b8f75e0a7f5b4a2f84f71d9fb0 Mon Sep 17 00:00:00 2001 From: ahopper Date: Sun, 1 Sep 2019 19:39:18 +0100 Subject: [PATCH 07/10] reduce repeated field access --- src/Avalonia.Base/AvaloniaObject.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 035e0056e0..c619d80e23 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -599,9 +599,10 @@ namespace Avalonia private object GetValueOrDefaultUnchecked(AvaloniaProperty property) { var aobj = this; - if (aobj._values != null) + var valuestore = aobj._values; + if (valuestore != null) { - var result = aobj._values.GetValue(property); + var result = valuestore.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; @@ -612,9 +613,10 @@ namespace Avalonia while(aobj.InheritanceParent is AvaloniaObject parent) { aobj = parent; - if (aobj._values != null) + valuestore = aobj._values; + if (valuestore != null) { - var result = aobj._values.GetValue(property); + var result = valuestore.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; From 66d79c025da7bfe9046def4c72e1514804d4c601 Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Sat, 7 Sep 2019 22:34:38 +0100 Subject: [PATCH 08/10] Update Default.xaml --- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index eaa267ba66..7b6870fec3 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -217,12 +217,12 @@ - + - + From 3dddb569821d2235b923662b8e6e4c590b940e59 Mon Sep 17 00:00:00 2001 From: ahopper Date: Sun, 8 Sep 2019 06:58:09 +0100 Subject: [PATCH 09/10] add space after while --- src/Avalonia.Base/AvaloniaObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index c619d80e23..94558c4367 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -610,7 +610,7 @@ namespace Avalonia } if (property.Inherits) { - while(aobj.InheritanceParent is AvaloniaObject parent) + while (aobj.InheritanceParent is AvaloniaObject parent) { aobj = parent; valuestore = aobj._values; From 45f86a925f4bf0766b40b209cfd42558a8a7bcc9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 8 Sep 2019 22:43:47 +0300 Subject: [PATCH 10/10] Fixed possible NRE in Gestures.cs --- src/Avalonia.Input/Gestures.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index bb8c8b8c40..ea4892ebfc 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -31,7 +31,7 @@ namespace Avalonia.Input RoutedEvent.Register( "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); - private static WeakReference s_lastPress; + private static WeakReference s_lastPress = new WeakReference(null); static Gestures() {