Просмотр исходного кода

Wire up validation/coercion for attached properties.

Steven Kirk 5 лет назад
Родитель
Сommit
7f26635efa

+ 5 - 3
src/Avalonia.Base/AttachedProperty.cs

@@ -18,12 +18,14 @@ namespace Avalonia
         /// <param name="ownerType">The class that is registering the property.</param>
         /// <param name="metadata">The property metadata.</param>
         /// <param name="inherits">Whether the property inherits its value.</param>
+        /// <param name="validate">A value validation callback.</param>
         public AttachedProperty(
             string name,
-            Type ownerType,            
+            Type ownerType,
             StyledPropertyMetadata<TValue> metadata,
-            bool inherits = false)
-            : base(name, ownerType, metadata, inherits)
+            bool inherits = false,
+            Func<TValue, bool> validate = null)
+            : base(name, ownerType, metadata, inherits, validate)
         {
         }
 

+ 14 - 6
src/Avalonia.Base/AvaloniaProperty.cs

@@ -304,22 +304,25 @@ namespace Avalonia
         /// <param name="inherits">Whether the property inherits its value.</param>
         /// <param name="defaultBindingMode">The default binding mode for the property.</param>
         /// <param name="validate">A value validation callback.</param>
+        /// <param name="coerce">A value coercion callback.</param>
         /// <returns>A <see cref="AvaloniaProperty{TValue}"/></returns>
         public static AttachedProperty<TValue> RegisterAttached<TOwner, THost, TValue>(
             string name,
             TValue defaultValue = default(TValue),
             bool inherits = false,
             BindingMode defaultBindingMode = BindingMode.OneWay,
-            Func<TValue, bool> validate = null)
+            Func<TValue, bool> validate = null,
+            Func<IAvaloniaObject, TValue, TValue> coerce = null)
                 where THost : IAvaloniaObject
         {
             Contract.Requires<ArgumentNullException>(name != null);
 
             var metadata = new StyledPropertyMetadata<TValue>(
                 defaultValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                coerce: coerce);
 
-            var result = new AttachedProperty<TValue>(name, typeof(TOwner), metadata, inherits);
+            var result = new AttachedProperty<TValue>(name, typeof(TOwner), metadata, inherits, validate);
             var registry = AvaloniaPropertyRegistry.Instance;
             registry.Register(typeof(TOwner), result);
             registry.RegisterAttached(typeof(THost), result);
@@ -336,22 +339,27 @@ namespace Avalonia
         /// <param name="defaultValue">The default value of the property.</param>
         /// <param name="inherits">Whether the property inherits its value.</param>
         /// <param name="defaultBindingMode">The default binding mode for the property.</param>
+        /// <param name="validate">A value validation callback.</param>
+        /// <param name="coerce">A value coercion callback.</param>
         /// <returns>A <see cref="AvaloniaProperty{TValue}"/></returns>
         public static AttachedProperty<TValue> RegisterAttached<THost, TValue>(
             string name,
             Type ownerType,
             TValue defaultValue = default(TValue),
             bool inherits = false,
-            BindingMode defaultBindingMode = BindingMode.OneWay)
+            BindingMode defaultBindingMode = BindingMode.OneWay,
+            Func<TValue, bool> validate = null,
+            Func<IAvaloniaObject, TValue, TValue> coerce = null)
                 where THost : IAvaloniaObject
         {
             Contract.Requires<ArgumentNullException>(name != null);
 
             var metadata = new StyledPropertyMetadata<TValue>(
                 defaultValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                coerce: coerce);
 
-            var result = new AttachedProperty<TValue>(name, ownerType, metadata, inherits);
+            var result = new AttachedProperty<TValue>(name, ownerType, metadata, inherits, validate);
             var registry = AvaloniaPropertyRegistry.Instance;
             registry.Register(ownerType, result);
             registry.RegisterAttached(typeof(THost), result);

+ 5 - 1
src/Avalonia.Controls/DefinitionBase.cs

@@ -358,7 +358,11 @@ namespace Avalonia.Controls
         /// </remarks>
         private static bool SharedSizeGroupPropertyValueValid(string value)
         {
-            Contract.Requires<ArgumentNullException>(value != null);
+            //  null is default value
+            if (value == null)
+            {
+                return true;
+            }
 
             string id = (string)value;
 

+ 16 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs

@@ -20,6 +20,16 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal(100, target.Foo);
         }
 
+        [Fact]
+        public void Coerces_Set_Value_Attached()
+        {
+            var target = new Class1();
+
+            target.SetValue(Class1.AttachedProperty, 150);
+
+            Assert.Equal(100, target.GetValue(Class1.AttachedProperty));
+        }
+
         [Fact]
         public void Coerces_Bound_Value()
         {
@@ -98,6 +108,12 @@ namespace Avalonia.Base.UnitTests
                     defaultValue: 11,
                     coerce: CoerceFoo);
 
+            public static readonly AttachedProperty<int> AttachedProperty =
+                AvaloniaProperty.RegisterAttached<Class1, AvaloniaObject, int>(
+                    "Attached",
+                    defaultValue: 11,
+                    coerce: CoerceFoo);
+
             public int Foo
             {
                 get => GetValue(FooProperty);

+ 15 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Reactive.Subjects;
+using Avalonia.Controls;
 using Xunit;
 
 namespace Avalonia.Base.UnitTests
@@ -34,6 +35,14 @@ namespace Avalonia.Base.UnitTests
             Assert.Throws<ArgumentException>(() => target.SetValue(Class1.FooProperty, 101));
         }
 
+        [Fact]
+        public void SetValue_Throws_If_Fails_Validation_Attached()
+        {
+            var target = new Class1();
+
+            Assert.Throws<ArgumentException>(() => target.SetValue(Class1.AttachedProperty, 101));
+        }
+
         [Fact]
         public void Reverts_To_DefaultValue_If_Binding_Fails_Validation()
         {
@@ -69,6 +78,12 @@ namespace Avalonia.Base.UnitTests
                     defaultValue: 11,
                     validate: ValidateFoo);
 
+            public static readonly AttachedProperty<int> AttachedProperty =
+                AvaloniaProperty.RegisterAttached<Class1, AvaloniaObject, int>(
+                    "Attached",
+                    defaultValue: 11,
+                    validate: ValidateFoo);
+
             public static bool ValidateFoo(int value)
             {
                 return value < 100;