Kaynağa Gözat

Merge pull request #2683 from AvaloniaUI/fixes/2372-slider-coercion

 Don't coerce RangeBase values until initialized.
Nikita Tsukanov 6 yıl önce
ebeveyn
işleme
a6f8e523bf

+ 43 - 22
src/Avalonia.Controls/Primitives/RangeBase.cs

@@ -75,10 +75,18 @@ namespace Avalonia.Controls.Primitives
 
             set
             {
-                value = ValidateMinimum(value);
-                SetAndRaise(MinimumProperty, ref _minimum, value);
-                Maximum = ValidateMaximum(Maximum);
-                Value = ValidateValue(Value);
+                ValidateDouble(value, "Minimum");
+
+                if (IsInitialized)
+                {
+                    SetAndRaise(MinimumProperty, ref _minimum, value);
+                    Maximum = ValidateMaximum(Maximum);
+                    Value = ValidateValue(Value);
+                }
+                else
+                {
+                    SetAndRaise(MinimumProperty, ref _minimum, value);
+                }
             }
         }
 
@@ -94,9 +102,18 @@ namespace Avalonia.Controls.Primitives
 
             set
             {
-                value = ValidateMaximum(value);
-                SetAndRaise(MaximumProperty, ref _maximum, value);
-                Value = ValidateValue(Value);
+                ValidateDouble(value, "Maximum");
+
+                if (IsInitialized)
+                {
+                    value = ValidateMaximum(value);
+                    SetAndRaise(MaximumProperty, ref _maximum, value);
+                    Value = ValidateValue(Value);
+                }
+                else
+                {
+                    SetAndRaise(MaximumProperty, ref _maximum, value);
+                }
             }
         }
 
@@ -112,8 +129,17 @@ namespace Avalonia.Controls.Primitives
 
             set
             {
-                value = ValidateValue(value);
-                SetAndRaise(ValueProperty, ref _value, value);
+                ValidateDouble(value, "Value");
+
+                if (IsInitialized)
+                {
+                    value = ValidateValue(value);
+                    SetAndRaise(ValueProperty, ref _value, value);
+                }
+                else
+                {
+                    SetAndRaise(ValueProperty, ref _value, value);
+                }
             }
         }
 
@@ -129,6 +155,14 @@ namespace Avalonia.Controls.Primitives
             set => SetValue(LargeChangeProperty, value);
         }
 
+        protected override void OnInitialized()
+        {
+            base.OnInitialized();
+
+            Maximum = ValidateMaximum(Maximum);
+            Value = ValidateValue(Value);
+        }
+
         /// <summary>
         /// Throws an exception if the double value is NaN or Inf.
         /// </summary>
@@ -142,17 +176,6 @@ namespace Avalonia.Controls.Primitives
             }
         }
 
-        /// <summary>
-        /// Validates the <see cref="Minimum"/> property.
-        /// </summary>
-        /// <param name="value">The value.</param>
-        /// <returns>The coerced value.</returns>
-        private double ValidateMinimum(double value)
-        {
-            ValidateDouble(value, "Minimum");
-            return value;
-        }
-
         /// <summary>
         /// Validates/coerces the <see cref="Maximum"/> property.
         /// </summary>
@@ -160,7 +183,6 @@ namespace Avalonia.Controls.Primitives
         /// <returns>The coerced value.</returns>
         private double ValidateMaximum(double value)
         {
-            ValidateDouble(value, "Maximum");
             return Math.Max(value, Minimum);
         }
 
@@ -171,7 +193,6 @@ namespace Avalonia.Controls.Primitives
         /// <returns>The coerced value.</returns>
         private double ValidateValue(double value)
         {
-            ValidateDouble(value, "Value");
             return MathUtilities.Clamp(value, Minimum, Maximum);
         }
     }

+ 9 - 1
src/Avalonia.Styling/StyledElement.cs

@@ -392,6 +392,7 @@ namespace Avalonia
             if (_initCount == 0 && !IsInitialized)
             {
                 IsInitialized = true;
+                OnInitialized();
                 Initialized?.Invoke(this, EventArgs.Empty);
             }
         }
@@ -608,7 +609,14 @@ namespace Avalonia
         protected virtual void OnDataContextEndUpdate()
         {
         }
-        
+
+        /// <summary>
+        /// Called when the control finishes initialization.
+        /// </summary>
+        protected virtual void OnInitialized()
+        {
+        }
+
         private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted)
         {
             if (o is StyledElement element)

+ 38 - 1
tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs

@@ -8,6 +8,7 @@ using Avalonia.Controls.Templates;
 using Avalonia.Data;
 using Avalonia.Markup.Data;
 using Avalonia.Styling;
+using Avalonia.UnitTests;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests.Primitives
@@ -22,6 +23,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Minimum = 100,
                 Maximum = 50,
             };
+            var root = new TestRoot(target);
 
             Assert.Equal(100, target.Minimum);
             Assert.Equal(100, target.Maximum);
@@ -36,6 +38,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Maximum = 50,
                 Value = 100,
             };
+            var root = new TestRoot(target);
 
             Assert.Equal(0, target.Minimum);
             Assert.Equal(50, target.Maximum);
@@ -51,6 +54,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Maximum = 100,
                 Value = 50,
             };
+            var root = new TestRoot(target);
 
             target.Minimum = 200;
 
@@ -68,6 +72,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Maximum = 100,
                 Value = 100,
             };
+            var root = new TestRoot(target);
 
             target.Maximum = 50;
 
@@ -160,6 +165,38 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Equal(expected, track.Value);
         }
 
+        [Fact]
+        public void Coercion_Should_Not_Be_Done_During_Initialization()
+        {
+            var target = new TestRange();
+
+            target.BeginInit();
+
+            var root = new TestRoot(target);
+            target.Minimum = 1;
+            Assert.Equal(0, target.Value);
+
+            target.Value = 50;
+            target.EndInit();
+
+            Assert.Equal(50, target.Value);
+        }
+
+        [Fact]
+        public void Coercion_Should_Be_Done_After_Initialization()
+        {
+            var target = new TestRange();
+
+            target.BeginInit();
+
+            var root = new TestRoot(target);
+            target.Minimum = 1;
+
+            target.EndInit();
+
+            Assert.Equal(1, target.Value);
+        }
+
         private class TestRange : RangeBase
         {
         }
@@ -199,4 +236,4 @@ namespace Avalonia.Controls.UnitTests.Primitives
             }
         }
     }
-}
+}

+ 19 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@@ -907,6 +907,25 @@ do we need it?")]
             }
         }
 
+        [Fact]
+        public void Slider_Properties_Can_Be_Set_In_Any_Order()
+        {
+            using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'>
+    <Slider Width='400' Value='500' Minimum='0' Maximum='1000'/>
+</Window>";
+
+                var window = AvaloniaXamlLoader.Parse<Window>(xaml);
+                var slider = (Slider)window.Content;
+
+                Assert.Equal(0, slider.Minimum);
+                Assert.Equal(1000, slider.Maximum);
+                Assert.Equal(500, slider.Value);
+            }
+        }
+
         private class SelectedItemsViewModel : INotifyPropertyChanged
         {
             public string[] Items { get; set; }