Browse Source

Added IValueEntry.GetDataValidationState.

Implemented only on `BindingEntryBase` as only bindings can supply data validation state.
pull/10423/head
Steven Kirk 4 years ago
parent
commit
7b2a659725
  1. 67
      src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
  2. 11
      src/Avalonia.Base/PropertyStore/IValueEntry.cs
  3. 8
      src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs
  4. 8
      src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs
  5. 7
      src/Avalonia.Base/Styling/Setter.cs

67
src/Avalonia.Base/PropertyStore/BindingEntryBase.cs

@ -14,23 +14,18 @@ namespace Avalonia.PropertyStore
{ {
private static readonly IDisposable s_creating = Disposable.Empty; private static readonly IDisposable s_creating = Disposable.Empty;
private static readonly IDisposable s_creatingQuiet = Disposable.Create(() => { }); private static readonly IDisposable s_creatingQuiet = Disposable.Create(() => { });
private readonly bool _hasDataValidation;
private IDisposable? _subscription; private IDisposable? _subscription;
private bool _hasValue; private bool _hasValue;
private TValue? _value; private TValue? _value;
private TValue? _defaultValue; private UncommonFields? _uncommon;
private bool _isDefaultValueInitialized;
protected BindingEntryBase( protected BindingEntryBase(
AvaloniaObject target, AvaloniaObject target,
ValueFrame frame, ValueFrame frame,
AvaloniaProperty property, AvaloniaProperty property,
IObservable<BindingValue<TSource>> source) IObservable<BindingValue<TSource>> source)
: this(target, frame, property, (object)source)
{ {
Frame = frame;
Source = source;
Property = property;
_hasDataValidation = property.GetMetadata(target.GetType()).EnableDataValidation ?? false;
} }
protected BindingEntryBase( protected BindingEntryBase(
@ -38,11 +33,21 @@ namespace Avalonia.PropertyStore
ValueFrame frame, ValueFrame frame,
AvaloniaProperty property, AvaloniaProperty property,
IObservable<TSource> source) IObservable<TSource> source)
: this(target, frame, property, (object)source)
{
}
private BindingEntryBase(
AvaloniaObject target,
ValueFrame frame,
AvaloniaProperty property,
object source)
{ {
Frame = frame; Frame = frame;
Source = source;
Property = property; Property = property;
_hasDataValidation = property.GetMetadata(target.GetType()).EnableDataValidation ?? false; Source = source;
if (property.GetMetadata(target.GetType()).EnableDataValidation == true)
_uncommon = new() { _hasDataValidation = true };
} }
public bool HasValue public bool HasValue
@ -74,6 +79,20 @@ namespace Avalonia.PropertyStore
return _value!; return _value!;
} }
public bool GetDataValidationState(out BindingValueType state, out Exception? error)
{
if (_uncommon?._hasDataValidation == true)
{
state = _uncommon._dataValidationState;
error = _uncommon._dataValidationError;
return true;
}
state = BindingValueType.Value;
error = null;
return false;
}
public void Start() => Start(true); public void Start() => Start(true);
public void OnCompleted() => BindingCompleted(); public void OnCompleted() => BindingCompleted();
@ -85,9 +104,6 @@ namespace Avalonia.PropertyStore
{ {
_subscription?.Dispose(); _subscription?.Dispose();
_subscription = null; _subscription = null;
if (_hasDataValidation)
Frame.Owner?.Owner.OnUpdateDataValidation(Property, BindingValueType.UnsetValue, null);
} }
object? IValueEntry.GetValue() object? IValueEntry.GetValue()
@ -132,6 +148,12 @@ namespace Avalonia.PropertyStore
if (!value.HasValue && value.Type != BindingValueType.DataValidationError) if (!value.HasValue && value.Type != BindingValueType.DataValidationError)
value = value.WithValue(instance.GetCachedDefaultValue()); value = value.WithValue(instance.GetCachedDefaultValue());
if (instance._uncommon?._hasDataValidation == true)
{
instance._uncommon._dataValidationState = value.Type;
instance._uncommon._dataValidationError = value.Error;
}
if (value.HasValue && if (value.HasValue &&
(!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, value.Value))) (!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, value.Value)))
{ {
@ -140,9 +162,6 @@ namespace Avalonia.PropertyStore
if (instance._subscription is not null && instance._subscription != s_creatingQuiet) if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority); instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority);
} }
if (instance._hasDataValidation)
owner.OnUpdateDataValidation(property, originalType, value.Error);
} }
if (value.Type == BindingValueType.DoNothing) if (value.Type == BindingValueType.DoNothing)
@ -170,13 +189,23 @@ namespace Avalonia.PropertyStore
private TValue GetCachedDefaultValue() private TValue GetCachedDefaultValue()
{ {
if (!_isDefaultValueInitialized) if (_uncommon?._isDefaultValueInitialized != true)
{ {
_defaultValue = GetDefaultValue(Frame.Owner!.Owner.GetType()); _uncommon ??= new();
_isDefaultValueInitialized = true; _uncommon._defaultValue = GetDefaultValue(Frame.Owner!.Owner.GetType());
_uncommon._isDefaultValueInitialized = true;
} }
return _defaultValue!; return _uncommon._defaultValue!;
}
private class UncommonFields
{
public TValue? _defaultValue;
public bool _isDefaultValueInitialized;
public bool _hasDataValidation;
public BindingValueType _dataValidationState;
public Exception? _dataValidationError;
} }
} }
} }

11
src/Avalonia.Base/PropertyStore/IValueEntry.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Data;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -22,6 +23,16 @@ namespace Avalonia.PropertyStore
/// </exception> /// </exception>
object? GetValue(); object? GetValue();
/// <summary>
/// Gets the data validation state if supported.
/// </summary>
/// <param name="state">The binding validation state.</param>
/// <param name="error">The current binding error, if any.</param>
/// <returns>
/// True if the entry supports data validation, otherwise false.
/// </returns>
bool GetDataValidationState(out BindingValueType state, out Exception? error);
/// <summary> /// <summary>
/// Called when the value entry is removed from the value store. /// Called when the value entry is removed from the value store.
/// </summary> /// </summary>

8
src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Data;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -27,5 +28,12 @@ namespace Avalonia.PropertyStore
object? IValueEntry.GetValue() => _value; object? IValueEntry.GetValue() => _value;
T IValueEntry<T>.GetValue() => _value; T IValueEntry<T>.GetValue() => _value;
bool IValueEntry.GetDataValidationState(out BindingValueType state, out Exception? error)
{
state = BindingValueType.Value;
error = null;
return false;
}
} }
} }

8
src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Data;
using Avalonia.PropertyStore; using Avalonia.PropertyStore;
namespace Avalonia.Styling namespace Avalonia.Styling
@ -19,6 +20,13 @@ namespace Avalonia.Styling
public object? GetValue() => _value ??= _template.Build(); public object? GetValue() => _value ??= _template.Build();
bool IValueEntry.GetDataValidationState(out BindingValueType state, out Exception? error)
{
state = BindingValueType.Value;
error = null;
return false;
}
void IValueEntry.Unsubscribe() { } void IValueEntry.Unsubscribe() { }
} }
} }

7
src/Avalonia.Base/Styling/Setter.cs

@ -90,6 +90,13 @@ namespace Avalonia.Styling
object? IValueEntry.GetValue() => Value; object? IValueEntry.GetValue() => Value;
bool IValueEntry.GetDataValidationState(out BindingValueType state, out Exception? error)
{
state = BindingValueType.Value;
error = null;
return false;
}
private AvaloniaProperty EnsureProperty() private AvaloniaProperty EnsureProperty()
{ {
return Property ?? throw new InvalidOperationException("Setter.Property must be set."); return Property ?? throw new InvalidOperationException("Setter.Property must be set.");

Loading…
Cancel
Save