Browse Source

Support data validation on style bindings.

pull/10423/head
Steven Kirk 4 years ago
parent
commit
172a75e438
  1. 28
      src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
  2. 6
      src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs
  3. 3
      src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs
  4. 6
      src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs
  5. 3
      src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs
  6. 2
      src/Avalonia.Base/Styling/PropertySetterBindingInstance.cs
  7. 3
      src/Avalonia.Base/Styling/Setter.cs

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

@ -3,6 +3,7 @@ using System.Collections.Generic;
using Avalonia.Reactive;
using Avalonia.Data;
using Avalonia.Threading;
using static Avalonia.Rendering.Composition.Animations.PropertySetSnapshot;
namespace Avalonia.PropertyStore
{
@ -13,6 +14,7 @@ namespace Avalonia.PropertyStore
{
private static readonly IDisposable s_creating = Disposable.Empty;
private static readonly IDisposable s_creatingQuiet = Disposable.Create(() => { });
private readonly bool _hasDataValidation;
private IDisposable? _subscription;
private bool _hasValue;
private TValue? _value;
@ -20,6 +22,7 @@ namespace Avalonia.PropertyStore
private bool _isDefaultValueInitialized;
protected BindingEntryBase(
AvaloniaObject target,
ValueFrame frame,
AvaloniaProperty property,
IObservable<BindingValue<TSource>> source)
@ -27,9 +30,11 @@ namespace Avalonia.PropertyStore
Frame = frame;
Source = source;
Property = property;
_hasDataValidation = property.GetMetadata(target.GetType()).EnableDataValidation ?? false;
}
protected BindingEntryBase(
AvaloniaObject target,
ValueFrame frame,
AvaloniaProperty property,
IObservable<TSource> source)
@ -37,6 +42,7 @@ namespace Avalonia.PropertyStore
Frame = frame;
Source = source;
Property = property;
_hasDataValidation = property.GetMetadata(target.GetType()).EnableDataValidation ?? false;
}
public bool HasValue
@ -79,6 +85,9 @@ namespace Avalonia.PropertyStore
{
_subscription?.Dispose();
_subscription = null;
if (_hasDataValidation)
Frame.Owner?.Owner.OnUpdateDataValidation(Property, BindingValueType.UnsetValue, null);
}
object? IValueEntry.GetValue()
@ -111,20 +120,29 @@ namespace Avalonia.PropertyStore
{
static void Execute(BindingEntryBase<TValue, TSource> instance, BindingValue<TValue> value)
{
if (instance.Frame.Owner is null)
if (instance.Frame.Owner is not { } valueStore)
return;
LoggingUtils.LogIfNecessary(instance.Frame.Owner.Owner, instance.Property, value);
var owner = valueStore.Owner;
var property = instance.Property;
var originalType = value.Type;
LoggingUtils.LogIfNecessary(owner, property, value);
var effectiveValue = value.HasValue ? value.Value : instance.GetCachedDefaultValue();
if (!value.HasValue && value.Type != BindingValueType.DataValidationError)
value = value.WithValue(instance.GetCachedDefaultValue());
if (!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, effectiveValue))
if (value.HasValue &&
(!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, value.Value)))
{
instance._value = effectiveValue;
instance._value = value.Value;
instance._hasValue = true;
if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority);
}
if (instance._hasDataValidation)
owner.OnUpdateDataValidation(property, originalType, value.Error);
}
if (value.Type == BindingValueType.DoNothing)

6
src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs

@ -18,7 +18,7 @@ namespace Avalonia.PropertyStore
StyledProperty<T> property,
IObservable<BindingValue<T>> source)
{
var e = new TypedBindingEntry<T>(this, property, source);
var e = new TypedBindingEntry<T>(Owner!.Owner, this, property, source);
Add(e);
return e;
}
@ -27,7 +27,7 @@ namespace Avalonia.PropertyStore
StyledProperty<T> property,
IObservable<T> source)
{
var e = new TypedBindingEntry<T>(this, property, source);
var e = new TypedBindingEntry<T>(Owner!.Owner, this, property, source);
Add(e);
return e;
}
@ -36,7 +36,7 @@ namespace Avalonia.PropertyStore
StyledProperty<T> property,
IObservable<object?> source)
{
var e = new SourceUntypedBindingEntry<T>(this, property, source);
var e = new SourceUntypedBindingEntry<T>(Owner!.Owner, this, property, source);
Add(e);
return e;
}

3
src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs

@ -12,10 +12,11 @@ namespace Avalonia.PropertyStore
private readonly Func<TTarget, bool>? _validate;
public SourceUntypedBindingEntry(
AvaloniaObject target,
ValueFrame frame,
StyledProperty<TTarget> property,
IObservable<object?> source)
: base(frame, property, source)
: base(target, frame, property, source)
{
_validate = property.ValidateValue;
}

6
src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs

@ -10,18 +10,20 @@ namespace Avalonia.PropertyStore
internal sealed class TypedBindingEntry<T> : BindingEntryBase<T, T>
{
public TypedBindingEntry(
AvaloniaObject target,
ValueFrame frame,
StyledProperty<T> property,
IObservable<T> source)
: base(frame, property, source)
: base(target, frame, property, source)
{
}
public TypedBindingEntry(
AvaloniaObject target,
ValueFrame frame,
StyledProperty<T> property,
IObservable<BindingValue<T>> source)
: base(frame, property, source)
: base(target, frame, property, source)
{
}

3
src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs

@ -12,10 +12,11 @@ namespace Avalonia.PropertyStore
private readonly Func<object?, bool>? _validate;
public UntypedBindingEntry(
AvaloniaObject target,
ValueFrame frame,
AvaloniaProperty property,
IObservable<object?> source)
: base(frame, property, source)
: base(target, frame, property, source)
{
_validate = ((IStyledPropertyAccessor)property).ValidateValue;
}

2
src/Avalonia.Base/Styling/PropertySetterBindingInstance.cs

@ -15,7 +15,7 @@ namespace Avalonia.Styling
AvaloniaProperty property,
BindingMode mode,
IObservable<object?> source)
: base(instance, property, source)
: base(target, instance, property, source)
{
_target = target;
_mode = mode;

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

@ -99,7 +99,8 @@ namespace Avalonia.Styling
{
if (!Property!.IsDirect)
{
var i = binding.Initiate(target, Property)!;
var hasDataValidation = Property.GetMetadata(target.GetType()).EnableDataValidation ?? false;
var i = binding.Initiate(target, Property, enableDataValidation: hasDataValidation)!;
var mode = i.Mode;
if (mode == BindingMode.Default)

Loading…
Cancel
Save