Browse Source

Moved EnableDataValdiation to AvaloniaPropertyMetadata

Support data validation in StyledProperty bindings

Co-Authored-By: Tom Edwards <109803929+tomenscape@users.noreply.github.com>
pull/10423/head
Steven Kirk 4 years ago
parent
commit
688dbc4fdc
  1. 11
      src/Avalonia.Base/AvaloniaObject.cs
  2. 4
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  3. 13
      src/Avalonia.Base/AvaloniaProperty.cs
  4. 18
      src/Avalonia.Base/AvaloniaPropertyMetadata.cs
  5. 27
      src/Avalonia.Base/DirectPropertyMetadata`1.cs
  6. 1
      src/Avalonia.Base/StyledElement.cs
  7. 6
      src/Avalonia.Base/StyledPropertyMetadata`1.cs
  8. 1
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

11
src/Avalonia.Base/AvaloniaObject.cs

@ -776,11 +776,16 @@ namespace Avalonia
break; break;
} }
var metadata = property.GetMetadata(GetType()); UpdateDataValidationCore(property, value.Type, value.Error);
}
if (metadata.EnableDataValidation == true) internal void UpdateDataValidationCore(AvaloniaProperty property,
BindingValueType state,
Exception? error)
{
if (property.GetMetadata(GetType()) is { EnableDataValidation: true })
{ {
UpdateDataValidation(property, value.Type, value.Error); UpdateDataValidation(property, state, error);
} }
} }

4
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -199,13 +199,11 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
binding = binding ?? throw new ArgumentNullException(nameof(binding)); binding = binding ?? throw new ArgumentNullException(nameof(binding));
var metadata = property.GetMetadata(target.GetType()) as IDirectPropertyMetadata;
var result = binding.Initiate( var result = binding.Initiate(
target, target,
property, property,
anchor, anchor,
metadata?.EnableDataValidation ?? false); property.GetMetadata(target.GetType()).EnableDataValidation ?? false);
if (result != null) if (result != null)
{ {

13
src/Avalonia.Base/AvaloniaProperty.cs

@ -227,6 +227,7 @@ namespace Avalonia
/// <param name="defaultBindingMode">The default binding mode for the property.</param> /// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="validate">A value validation callback.</param> /// <param name="validate">A value validation callback.</param>
/// <param name="coerce">A value coercion callback.</param> /// <param name="coerce">A value coercion callback.</param>
/// <param name="enableDataValidation">Whether the property is interested in data validation.</param>
/// <returns>A <see cref="StyledProperty{TValue}"/></returns> /// <returns>A <see cref="StyledProperty{TValue}"/></returns>
public static StyledProperty<TValue> Register<TOwner, TValue>( public static StyledProperty<TValue> Register<TOwner, TValue>(
string name, string name,
@ -234,7 +235,8 @@ namespace Avalonia
bool inherits = false, bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay, BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null, Func<TValue, bool>? validate = null,
Func<AvaloniaObject, TValue, TValue>? coerce = null) Func<AvaloniaObject, TValue, TValue>? coerce = null,
bool enableDataValidation = false)
where TOwner : AvaloniaObject where TOwner : AvaloniaObject
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -242,7 +244,8 @@ namespace Avalonia
var metadata = new StyledPropertyMetadata<TValue>( var metadata = new StyledPropertyMetadata<TValue>(
defaultValue, defaultValue,
defaultBindingMode: defaultBindingMode, defaultBindingMode: defaultBindingMode,
coerce: coerce); coerce: coerce,
enableDataValidation: enableDataValidation);
var result = new StyledProperty<TValue>( var result = new StyledProperty<TValue>(
name, name,
@ -253,7 +256,7 @@ namespace Avalonia
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result); AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result; return result;
} }
/// <inheritdoc cref="Register{TOwner, TValue}" /> /// <inheritdoc cref="Register{TOwner, TValue}" />
/// <param name="notifying"> /// <param name="notifying">
/// A method that gets called before and after the property starts being notified on an /// A method that gets called before and after the property starts being notified on an
@ -267,6 +270,7 @@ namespace Avalonia
BindingMode defaultBindingMode, BindingMode defaultBindingMode,
Func<TValue, bool>? validate, Func<TValue, bool>? validate,
Func<AvaloniaObject, TValue, TValue>? coerce, Func<AvaloniaObject, TValue, TValue>? coerce,
bool enableDataValidation,
Action<AvaloniaObject, bool>? notifying) Action<AvaloniaObject, bool>? notifying)
where TOwner : AvaloniaObject where TOwner : AvaloniaObject
{ {
@ -275,7 +279,8 @@ namespace Avalonia
var metadata = new StyledPropertyMetadata<TValue>( var metadata = new StyledPropertyMetadata<TValue>(
defaultValue, defaultValue,
defaultBindingMode: defaultBindingMode, defaultBindingMode: defaultBindingMode,
coerce: coerce); coerce: coerce,
enableDataValidation: enableDataValidation);
var result = new StyledProperty<TValue>( var result = new StyledProperty<TValue>(
name, name,

18
src/Avalonia.Base/AvaloniaPropertyMetadata.cs

@ -13,10 +13,13 @@ namespace Avalonia
/// Initializes a new instance of the <see cref="AvaloniaPropertyMetadata"/> class. /// Initializes a new instance of the <see cref="AvaloniaPropertyMetadata"/> class.
/// </summary> /// </summary>
/// <param name="defaultBindingMode">The default binding mode.</param> /// <param name="defaultBindingMode">The default binding mode.</param>
/// <param name="enableDataValidation">Whether the property is interested in data validation.</param>
public AvaloniaPropertyMetadata( public AvaloniaPropertyMetadata(
BindingMode defaultBindingMode = BindingMode.Default) BindingMode defaultBindingMode = BindingMode.Default,
bool? enableDataValidation = null)
{ {
_defaultBindingMode = defaultBindingMode; _defaultBindingMode = defaultBindingMode;
EnableDataValidation = enableDataValidation;
} }
/// <summary> /// <summary>
@ -31,6 +34,17 @@ namespace Avalonia
} }
} }
/// <summary>
/// Gets a value indicating whether the property is interested in data validation.
/// </summary>
/// <remarks>
/// Data validation is validation performed at the target of a binding, for example in a
/// view model using the INotifyDataErrorInfo interface. Only certain properties on a
/// control (such as a TextBox's Text property) will be interested in receiving data
/// validation messages so this feature must be explicitly enabled by setting this flag.
/// </remarks>
public bool? EnableDataValidation { get; private set; }
/// <summary> /// <summary>
/// Merges the metadata with the base metadata. /// Merges the metadata with the base metadata.
/// </summary> /// </summary>
@ -44,6 +58,8 @@ namespace Avalonia
{ {
_defaultBindingMode = baseMetadata.DefaultBindingMode; _defaultBindingMode = baseMetadata.DefaultBindingMode;
} }
EnableDataValidation ??= baseMetadata.EnableDataValidation;
} }
} }
} }

27
src/Avalonia.Base/DirectPropertyMetadata`1.cs

@ -21,10 +21,9 @@ namespace Avalonia
TValue unsetValue = default!, TValue unsetValue = default!,
BindingMode defaultBindingMode = BindingMode.Default, BindingMode defaultBindingMode = BindingMode.Default,
bool? enableDataValidation = null) bool? enableDataValidation = null)
: base(defaultBindingMode) : base(defaultBindingMode, enableDataValidation)
{ {
UnsetValue = unsetValue; UnsetValue = unsetValue;
EnableDataValidation = enableDataValidation;
} }
/// <summary> /// <summary>
@ -32,16 +31,6 @@ namespace Avalonia
/// </summary> /// </summary>
public TValue UnsetValue { get; private set; } public TValue UnsetValue { get; private set; }
/// <summary>
/// Gets a value indicating whether the property is interested in data validation.
/// </summary>
/// <remarks>
/// Data validation is validation performed at the target of a binding, for example in a
/// view model using the INotifyDataErrorInfo interface. Only certain properties on a
/// control (such as a TextBox's Text property) will be interested in receiving data
/// validation messages so this feature must be explicitly enabled by setting this flag.
/// </remarks>
public bool? EnableDataValidation { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
object? IDirectPropertyMetadata.UnsetValue => UnsetValue; object? IDirectPropertyMetadata.UnsetValue => UnsetValue;
@ -51,19 +40,9 @@ namespace Avalonia
{ {
base.Merge(baseMetadata, property); base.Merge(baseMetadata, property);
var src = baseMetadata as DirectPropertyMetadata<TValue>; if (baseMetadata is DirectPropertyMetadata<TValue> src)
if (src != null)
{ {
if (UnsetValue == null) UnsetValue ??= src.UnsetValue;
{
UnsetValue = src.UnsetValue;
}
if (EnableDataValidation == null)
{
EnableDataValidation = src.EnableDataValidation;
}
} }
} }
} }

1
src/Avalonia.Base/StyledElement.cs

@ -46,6 +46,7 @@ namespace Avalonia
defaultBindingMode: BindingMode.OneWay, defaultBindingMode: BindingMode.OneWay,
validate: null, validate: null,
coerce: null, coerce: null,
enableDataValidation: false,
notifying: DataContextNotifying); notifying: DataContextNotifying);
/// <summary> /// <summary>

6
src/Avalonia.Base/StyledPropertyMetadata`1.cs

@ -16,11 +16,13 @@ namespace Avalonia
/// <param name="defaultValue">The default value of the property.</param> /// <param name="defaultValue">The default value of the property.</param>
/// <param name="defaultBindingMode">The default binding mode.</param> /// <param name="defaultBindingMode">The default binding mode.</param>
/// <param name="coerce">A value coercion callback.</param> /// <param name="coerce">A value coercion callback.</param>
/// <param name="enableDataValidation">Whether the property is interested in data validation.</param>
public StyledPropertyMetadata( public StyledPropertyMetadata(
Optional<TValue> defaultValue = default, Optional<TValue> defaultValue = default,
BindingMode defaultBindingMode = BindingMode.Default, BindingMode defaultBindingMode = BindingMode.Default,
Func<AvaloniaObject, TValue, TValue>? coerce = null) Func<AvaloniaObject, TValue, TValue>? coerce = null,
: base(defaultBindingMode) bool enableDataValidation = false)
: base(defaultBindingMode, enableDataValidation)
{ {
_defaultValue = defaultValue; _defaultValue = defaultValue;
CoerceValue = coerce; CoerceValue = coerce;

1
tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

@ -198,6 +198,7 @@ namespace Avalonia.Base.UnitTests
defaultBindingMode: BindingMode.OneWay, defaultBindingMode: BindingMode.OneWay,
validate: null, validate: null,
coerce: null, coerce: null,
enableDataValidation: false,
notifying: FooNotifying); notifying: FooNotifying);
public int NotifyCount { get; private set; } public int NotifyCount { get; private set; }

Loading…
Cancel
Save