فهرست منبع

Merge pull request #10039 from Enscape/fixes/referencetype-binding-null-behaviour

Never convert null to UnsetValue in bindings
Steven Kirk 2 سال پیش
والد
کامیت
4d8102d9c0

+ 1 - 1
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@@ -30,7 +30,7 @@ namespace Avalonia.Data.Converters
         {
             if (value == null)
             {
-                return targetType.IsValueType ? AvaloniaProperty.UnsetValue : null;
+                return null;
             }
 
             if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1)

+ 1 - 1
src/Avalonia.Controls/Primitives/ToggleButton.cs

@@ -20,7 +20,7 @@ namespace Avalonia.Controls.Primitives
                 nameof(IsChecked),
                 o => o.IsChecked,
                 (o, v) => o.IsChecked = v,
-                unsetValue: null,
+                unsetValue: false,
                 defaultBindingMode: BindingMode.TwoWay);
 
         /// <summary>

+ 0 - 25
tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs

@@ -78,18 +78,6 @@ namespace Avalonia.Base.UnitTests.Data.Core
             GC.KeepAlive(data);
         }
 
-        [Fact]
-        public async Task Should_Coerce_Get_Null_Double_String_To_UnsetValue()
-        {
-            var data = new Class1 { StringValue = null };
-            var target = new BindingExpression(ExpressionObserver.Create(data, o => o.StringValue), typeof(double));
-            var result = await target.Take(1);
-
-            Assert.Equal(AvaloniaProperty.UnsetValue, result);
-
-            GC.KeepAlive(data);
-        }
-
         [Fact]
         public void Should_Convert_Set_String_To_Double()
         {
@@ -249,19 +237,6 @@ namespace Avalonia.Base.UnitTests.Data.Core
             GC.KeepAlive(data);
         }
 
-        [Fact]
-        public void Should_Coerce_Setting_Null_Double_To_Default_Value()
-        {
-            var data = new Class1 { DoubleValue = 5.6 };
-            var target = new BindingExpression(ExpressionObserver.Create(data, o => o.DoubleValue), typeof(string));
-
-            target.OnNext(null);
-
-            Assert.Equal(0, data.DoubleValue);
-
-            GC.KeepAlive(data);
-        }
-
         [Fact]
         public void Should_Coerce_Setting_UnsetValue_Double_To_Default_Value()
         {

+ 69 - 1
tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs

@@ -648,16 +648,69 @@ namespace Avalonia.Markup.UnitTests.Data
             };
         }
 
+        [Fact]
+        public void Binding_Producing_Default_Value_Should_Result_In_Correct_Priority()
+        {
+            var defaultValue = StyledPropertyClass.NullableDoubleProperty.GetDefaultValue(typeof(StyledPropertyClass));
+
+            var vm = new NullableValuesViewModel() { NullableDouble = defaultValue };
+            var target = new StyledPropertyClass();
+
+            target.Bind(StyledPropertyClass.NullableDoubleProperty, new Binding(nameof(NullableValuesViewModel.NullableDouble)) { Source = vm });
+
+            Assert.Equal(BindingPriority.LocalValue, target.GetDiagnosticInternal(StyledPropertyClass.NullableDoubleProperty).Priority);
+            Assert.Equal(defaultValue, target.GetValue(StyledPropertyClass.NullableDoubleProperty));
+        }
+
+        [Fact]
+        public void Binding_Non_Nullable_ValueType_To_Null_Reverts_To_Default_Value()
+        {
+            var source = new NullableValuesViewModel { NullableDouble = 42 };
+            var target = new StyledPropertyClass();
+            var binding = new Binding(nameof(source.NullableDouble)) { Source = source };
+
+            target.Bind(StyledPropertyClass.DoubleValueProperty, binding);
+            Assert.Equal(42, target.DoubleValue);
+
+            source.NullableDouble = null;
+
+            Assert.Equal(12.3, target.DoubleValue);
+        }
+
+        [Fact]
+        public void Binding_Nullable_ValueType_To_Null_Sets_Value_To_Null()
+        {
+            var source = new NullableValuesViewModel { NullableDouble = 42 };
+            var target = new StyledPropertyClass();
+            var binding = new Binding(nameof(source.NullableDouble)) { Source = source };
+
+            target.Bind(StyledPropertyClass.NullableDoubleProperty, binding);
+            Assert.Equal(42, target.NullableDouble);
+
+            source.NullableDouble = null;
+
+            Assert.Null(target.NullableDouble);
+        }
+
         private class StyledPropertyClass : AvaloniaObject
         {
             public static readonly StyledProperty<double> DoubleValueProperty =
-                        AvaloniaProperty.Register<StyledPropertyClass, double>(nameof(DoubleValue));
+                        AvaloniaProperty.Register<StyledPropertyClass, double>(nameof(DoubleValue), 12.3);
 
             public double DoubleValue
             {
                 get { return GetValue(DoubleValueProperty); }
                 set { SetValue(DoubleValueProperty, value); }
             }
+            
+            public static StyledProperty<double?> NullableDoubleProperty = 
+                AvaloniaProperty.Register<StyledPropertyClass, double?>(nameof(NullableDoubleProperty), -1);
+
+            public double? NullableDouble
+            {
+                get => GetValue(NullableDoubleProperty);
+                set => SetValue(NullableDoubleProperty, value);
+            }
         }
 
         private class DirectPropertyClass : AvaloniaObject
@@ -676,6 +729,21 @@ namespace Avalonia.Markup.UnitTests.Data
             }
         }
 
+        private class NullableValuesViewModel : INotifyPropertyChanged
+        {
+            public event PropertyChangedEventHandler PropertyChanged;
+
+            private double? _nullableDouble;
+            public double? NullableDouble
+            {
+                get => _nullableDouble; set
+                {
+                    _nullableDouble = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NullableDouble)));
+                }
+            }
+        }
+
         private class TestStackOverflowViewModel : INotifyPropertyChanged
         {
             public int SetterInvokedCount { get; private set; }