diff --git a/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs b/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
index 2fe3844a74..a841803ee1 100644
--- a/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
+++ b/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Avalonia.Reactive;
using Avalonia.Data;
using Avalonia.Threading;
-using static Avalonia.Rendering.Composition.Animations.PropertySetSnapshot;
namespace Avalonia.PropertyStore
{
diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue.cs
index 78f0ad46b7..11a4dd7893 100644
--- a/src/Avalonia.Base/PropertyStore/EffectiveValue.cs
+++ b/src/Avalonia.Base/PropertyStore/EffectiveValue.cs
@@ -11,9 +11,6 @@ namespace Avalonia.PropertyStore
///
internal abstract class EffectiveValue
{
- private IValueEntry? _valueEntry;
- private IValueEntry? _baseValueEntry;
-
///
/// Gets the current effective value as a boxed value.
///
@@ -29,6 +26,16 @@ namespace Avalonia.PropertyStore
///
public BindingPriority BasePriority { get; protected set; }
+ ///
+ /// Gets the active value entry for the current effective value.
+ ///
+ public IValueEntry? ValueEntry { get; private set; }
+
+ ///
+ /// Gets the active value entry for the current base value.
+ ///
+ public IValueEntry? BaseValueEntry { get; private set; }
+
///
/// Gets a value indicating whether the was overridden by a call to
/// .
@@ -63,14 +70,14 @@ namespace Avalonia.PropertyStore
{
if (Priority == BindingPriority.Unset)
{
- _valueEntry?.Unsubscribe();
- _valueEntry = null;
+ ValueEntry?.Unsubscribe();
+ ValueEntry = null;
}
if (BasePriority == BindingPriority.Unset)
{
- _baseValueEntry?.Unsubscribe();
- _baseValueEntry = null;
+ BaseValueEntry?.Unsubscribe();
+ BaseValueEntry = null;
}
}
@@ -135,40 +142,34 @@ namespace Avalonia.PropertyStore
// value, then the current entry becomes our base entry.
if (Priority > BindingPriority.LocalValue && Priority < BindingPriority.Inherited)
{
- Debug.Assert(_valueEntry is not null);
- _baseValueEntry = _valueEntry;
- _valueEntry = null;
+ Debug.Assert(ValueEntry is not null);
+ BaseValueEntry = ValueEntry;
+ ValueEntry = null;
}
- if (_valueEntry != entry)
+ if (ValueEntry != entry)
{
- _valueEntry?.Unsubscribe();
- _valueEntry = entry;
+ ValueEntry?.Unsubscribe();
+ ValueEntry = entry;
}
}
else if (Priority <= BindingPriority.Animation)
{
// We've received a non-animation value and have an active animation value, so the
// new entry becomes our base entry.
- if (_baseValueEntry != entry)
+ if (BaseValueEntry != entry)
{
- _baseValueEntry?.Unsubscribe();
- _baseValueEntry = entry;
+ BaseValueEntry?.Unsubscribe();
+ BaseValueEntry = entry;
}
}
- else if (_valueEntry != entry)
+ else if (ValueEntry != entry)
{
// Both the current value and the new value are non-animation values, so the new
// entry replaces the existing entry.
- _valueEntry?.Unsubscribe();
- _valueEntry = entry;
+ ValueEntry?.Unsubscribe();
+ ValueEntry = entry;
}
}
-
- protected void UnsubscribeValueEntries()
- {
- _valueEntry?.Unsubscribe();
- _baseValueEntry?.Unsubscribe();
- }
}
}
diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
index c469034f9b..0788b39459 100644
--- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
+++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Data;
+using static Avalonia.Rendering.Composition.Animations.PropertySetSnapshot;
namespace Avalonia.PropertyStore
{
@@ -61,6 +62,12 @@ namespace Avalonia.PropertyStore
UpdateValueEntry(value, priority);
SetAndRaiseCore(owner, (StyledProperty)value.Property, GetValue(value), priority, false);
+
+ if (priority > BindingPriority.LocalValue &&
+ value.GetDataValidationState(out var state, out var error))
+ {
+ owner.Owner.OnUpdateDataValidation(value.Property, state, error);
+ }
}
public void SetLocalValueAndRaise(
@@ -128,12 +135,10 @@ namespace Avalonia.PropertyStore
public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property)
{
- UnsubscribeValueEntries();
- DisposeAndRaiseUnset(owner, (StyledProperty)property);
- }
+ ValueEntry?.Unsubscribe();
+ BaseValueEntry?.Unsubscribe();
- public void DisposeAndRaiseUnset(ValueStore owner, StyledProperty property)
- {
+ var p = (StyledProperty)property;
BindingPriority priority;
T oldValue;
@@ -150,9 +155,16 @@ namespace Avalonia.PropertyStore
if (!EqualityComparer.Default.Equals(oldValue, Value))
{
- owner.Owner.RaisePropertyChanged(property, Value, oldValue, priority, true);
+ owner.Owner.RaisePropertyChanged(p, Value, oldValue, priority, true);
if (property.Inherits)
- owner.OnInheritedEffectiveValueDisposed(property, Value);
+ owner.OnInheritedEffectiveValueDisposed(p, Value);
+ }
+
+ if (ValueEntry?.GetDataValidationState(out _, out _) ??
+ BaseValueEntry?.GetDataValidationState(out _, out _) ??
+ false)
+ {
+ owner.Owner.OnUpdateDataValidation(p, BindingValueType.UnsetValue, null);
}
}
diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs
index 0a5084466f..0887f11ec9 100644
--- a/src/Avalonia.Base/PropertyStore/ValueStore.cs
+++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs
@@ -838,8 +838,6 @@ namespace Avalonia.PropertyStore
break;
}
- current?.EndReevaluation();
-
if (current?.Priority == BindingPriority.Unset)
{
if (current.BasePriority == BindingPriority.Unset)
@@ -852,6 +850,8 @@ namespace Avalonia.PropertyStore
current.RemoveAnimationAndRaise(this, property);
}
}
+
+ current?.EndReevaluation();
}
finally
{
@@ -923,7 +923,6 @@ namespace Avalonia.PropertyStore
for (var i = _effectiveValues.Count - 1; i >= 0; --i)
{
_effectiveValues.GetKeyValue(i, out var key, out var e);
- e.EndReevaluation();
if (e.Priority == BindingPriority.Unset)
{
@@ -933,6 +932,8 @@ namespace Avalonia.PropertyStore
if (i > _effectiveValues.Count)
break;
}
+
+ e.EndReevaluation();
}
}
finally
diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs
index b76c86e814..5de703deb1 100644
--- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs
+++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs
@@ -89,9 +89,10 @@ namespace Avalonia.Markup.UnitTests.Data
Mode = BindingMode.TwoWay
};
+ var model = new IndeiValidatingModel();
var root = new TestRoot
{
- DataContext = new IndeiValidatingModel(),
+ DataContext = model,
Styles =
{
new Style(x => x.Is())
@@ -109,13 +110,13 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(20, target.GetValue(property));
- target.SetValue(property, 200);
+ model.Value = 200;
Assert.Equal(200, target.GetValue(property));
Assert.IsType(target.DataValidationError);
Assert.Equal("Invalid value: 200.", target.DataValidationError?.Message);
- target.SetValue(property, 10);
+ model.Value = 10;
Assert.Equal(10, target.GetValue(property));
Assert.Null(target.DataValidationError);
@@ -166,7 +167,7 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.IsType(target.DataValidationError);
Assert.Equal("Invalid value: 200.", target.DataValidationError?.Message);
- target.SetValue(property, 10);
+ model.Value = 10;
Assert.Equal(10, target.GetValue(property));
Assert.Null(target.DataValidationError);