Browse Source

Wire up validation/coercion for attached properties.

pull/3287/head
Steven Kirk 6 years ago
parent
commit
7f26635efa
  1. 8
      src/Avalonia.Base/AttachedProperty.cs
  2. 20
      src/Avalonia.Base/AvaloniaProperty.cs
  3. 6
      src/Avalonia.Controls/DefinitionBase.cs
  4. 16
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs
  5. 15
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs

8
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)
{
}

20
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);

6
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
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
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;

Loading…
Cancel
Save