diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index d89d6f3690..bf1624eab3 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -776,11 +776,16 @@ namespace Avalonia 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); } } diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 6231483ff8..9fbf680a5c 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -199,13 +199,11 @@ namespace Avalonia property = property ?? throw new ArgumentNullException(nameof(property)); binding = binding ?? throw new ArgumentNullException(nameof(binding)); - var metadata = property.GetMetadata(target.GetType()) as IDirectPropertyMetadata; - var result = binding.Initiate( target, property, anchor, - metadata?.EnableDataValidation ?? false); + property.GetMetadata(target.GetType()).EnableDataValidation ?? false); if (result != null) { diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 45ab293a89..24244c5068 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -227,6 +227,7 @@ namespace Avalonia /// The default binding mode for the property. /// A value validation callback. /// A value coercion callback. + /// Whether the property is interested in data validation. /// A public static StyledProperty Register( string name, @@ -234,7 +235,8 @@ namespace Avalonia bool inherits = false, BindingMode defaultBindingMode = BindingMode.OneWay, Func? validate = null, - Func? coerce = null) + Func? coerce = null, + bool enableDataValidation = false) where TOwner : AvaloniaObject { _ = name ?? throw new ArgumentNullException(nameof(name)); @@ -242,7 +244,8 @@ namespace Avalonia var metadata = new StyledPropertyMetadata( defaultValue, defaultBindingMode: defaultBindingMode, - coerce: coerce); + coerce: coerce, + enableDataValidation: enableDataValidation); var result = new StyledProperty( name, @@ -253,7 +256,7 @@ namespace Avalonia AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result); return result; } - + /// /// /// A method that gets called before and after the property starts being notified on an @@ -267,6 +270,7 @@ namespace Avalonia BindingMode defaultBindingMode, Func? validate, Func? coerce, + bool enableDataValidation, Action? notifying) where TOwner : AvaloniaObject { @@ -275,7 +279,8 @@ namespace Avalonia var metadata = new StyledPropertyMetadata( defaultValue, defaultBindingMode: defaultBindingMode, - coerce: coerce); + coerce: coerce, + enableDataValidation: enableDataValidation); var result = new StyledProperty( name, diff --git a/src/Avalonia.Base/AvaloniaPropertyMetadata.cs b/src/Avalonia.Base/AvaloniaPropertyMetadata.cs index 2963567b14..62bb65351f 100644 --- a/src/Avalonia.Base/AvaloniaPropertyMetadata.cs +++ b/src/Avalonia.Base/AvaloniaPropertyMetadata.cs @@ -13,10 +13,13 @@ namespace Avalonia /// Initializes a new instance of the class. /// /// The default binding mode. + /// Whether the property is interested in data validation. public AvaloniaPropertyMetadata( - BindingMode defaultBindingMode = BindingMode.Default) + BindingMode defaultBindingMode = BindingMode.Default, + bool? enableDataValidation = null) { _defaultBindingMode = defaultBindingMode; + EnableDataValidation = enableDataValidation; } /// @@ -31,6 +34,17 @@ namespace Avalonia } } + /// + /// Gets a value indicating whether the property is interested in data validation. + /// + /// + /// 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. + /// + public bool? EnableDataValidation { get; private set; } + /// /// Merges the metadata with the base metadata. /// @@ -44,6 +58,8 @@ namespace Avalonia { _defaultBindingMode = baseMetadata.DefaultBindingMode; } + + EnableDataValidation ??= baseMetadata.EnableDataValidation; } } } diff --git a/src/Avalonia.Base/DirectPropertyMetadata`1.cs b/src/Avalonia.Base/DirectPropertyMetadata`1.cs index fe1cdd0e65..451ff6ce00 100644 --- a/src/Avalonia.Base/DirectPropertyMetadata`1.cs +++ b/src/Avalonia.Base/DirectPropertyMetadata`1.cs @@ -21,10 +21,9 @@ namespace Avalonia TValue unsetValue = default!, BindingMode defaultBindingMode = BindingMode.Default, bool? enableDataValidation = null) - : base(defaultBindingMode) + : base(defaultBindingMode, enableDataValidation) { UnsetValue = unsetValue; - EnableDataValidation = enableDataValidation; } /// @@ -32,16 +31,6 @@ namespace Avalonia /// public TValue UnsetValue { get; private set; } - /// - /// Gets a value indicating whether the property is interested in data validation. - /// - /// - /// 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. - /// - public bool? EnableDataValidation { get; private set; } /// object? IDirectPropertyMetadata.UnsetValue => UnsetValue; @@ -51,19 +40,9 @@ namespace Avalonia { base.Merge(baseMetadata, property); - var src = baseMetadata as DirectPropertyMetadata; - - if (src != null) + if (baseMetadata is DirectPropertyMetadata src) { - if (UnsetValue == null) - { - UnsetValue = src.UnsetValue; - } - - if (EnableDataValidation == null) - { - EnableDataValidation = src.EnableDataValidation; - } + UnsetValue ??= src.UnsetValue; } } } diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 5b8dac2f53..10a0c5da0e 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -46,6 +46,7 @@ namespace Avalonia defaultBindingMode: BindingMode.OneWay, validate: null, coerce: null, + enableDataValidation: false, notifying: DataContextNotifying); /// diff --git a/src/Avalonia.Base/StyledPropertyMetadata`1.cs b/src/Avalonia.Base/StyledPropertyMetadata`1.cs index c71973fde8..6f10de3651 100644 --- a/src/Avalonia.Base/StyledPropertyMetadata`1.cs +++ b/src/Avalonia.Base/StyledPropertyMetadata`1.cs @@ -16,11 +16,13 @@ namespace Avalonia /// The default value of the property. /// The default binding mode. /// A value coercion callback. + /// Whether the property is interested in data validation. public StyledPropertyMetadata( Optional defaultValue = default, BindingMode defaultBindingMode = BindingMode.Default, - Func? coerce = null) - : base(defaultBindingMode) + Func? coerce = null, + bool enableDataValidation = false) + : base(defaultBindingMode, enableDataValidation) { _defaultValue = defaultValue; CoerceValue = coerce; diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs index 7e932373c2..181596a681 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs @@ -198,6 +198,7 @@ namespace Avalonia.Base.UnitTests defaultBindingMode: BindingMode.OneWay, validate: null, coerce: null, + enableDataValidation: false, notifying: FooNotifying); public int NotifyCount { get; private set; }