diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs index 0d93e9d8ed..c469034f9b 100644 --- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs @@ -19,13 +19,16 @@ namespace Avalonia.PropertyStore private T? _baseValue; private UncommonFields? _uncommon; - public EffectiveValue(AvaloniaObject owner, StyledProperty property) + public EffectiveValue( + AvaloniaObject owner, + StyledProperty property, + EffectiveValue? inherited) { Priority = BindingPriority.Unset; BasePriority = BindingPriority.Unset; _metadata = property.GetMetadata(owner.GetType()); - var value = _metadata.DefaultValue; + var value = inherited is null ? _metadata.DefaultValue : inherited.Value; if (property.HasCoercion && _metadata.CoerceValue is { } coerce) { diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 8b702665f8..ec6ed392c1 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -184,7 +184,7 @@ namespace Avalonia.PropertyStore } else { - var effectiveValue = new EffectiveValue(Owner, property); + var effectiveValue = CreateEffectiveValue(property); AddEffectiveValue(property, effectiveValue); effectiveValue.SetAndRaise(this, result, priority); } @@ -200,7 +200,7 @@ namespace Avalonia.PropertyStore } else { - var effectiveValue = new EffectiveValue(Owner, property); + var effectiveValue = CreateEffectiveValue(property); AddEffectiveValue(property, effectiveValue); effectiveValue.SetLocalValueAndRaise(this, property, value); } @@ -217,7 +217,7 @@ namespace Avalonia.PropertyStore } else { - var effectiveValue = new EffectiveValue(Owner, property); + var effectiveValue = CreateEffectiveValue(property); AddEffectiveValue(property, effectiveValue); effectiveValue.SetCurrentValueAndRaise(this, property, value); } @@ -287,6 +287,16 @@ namespace Avalonia.PropertyStore return false; } + public EffectiveValue CreateEffectiveValue(StyledProperty property) + { + EffectiveValue? inherited = null; + + if (property.Inherits && TryGetInheritedValue(property, out var v)) + inherited = (EffectiveValue)v; + + return new EffectiveValue(Owner, property, inherited); + } + public void SetInheritanceParent(AvaloniaObject? newParent) { var values = AvaloniaPropertyDictionaryPool.Get(); @@ -799,7 +809,7 @@ namespace Avalonia.PropertyStore // - The value is a non-animation value and its priority is higher than the current // effective value's base priority var isRelevantPriority = current is null || - priority < current.Priority || + (priority < current.Priority && priority < current.BasePriority) || (priority > BindingPriority.Animation && priority < current.BasePriority); if (foundEntry && isRelevantPriority && entry!.HasValue) diff --git a/src/Avalonia.Base/StyledProperty.cs b/src/Avalonia.Base/StyledProperty.cs index ad1f09066e..8695918c18 100644 --- a/src/Avalonia.Base/StyledProperty.cs +++ b/src/Avalonia.Base/StyledProperty.cs @@ -171,7 +171,7 @@ namespace Avalonia internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) { - return new EffectiveValue(o, this); + return o.GetValueStore().CreateEffectiveValue(this); } /// diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index baaed5104d..b6036bba8f 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -167,6 +167,22 @@ namespace Avalonia.Base.UnitTests Assert.Equal("foo", target.GetValue(property)); } + [Fact] + public void Completing_Animation_Binding_Reverts_To_Set_LocalValue_With_Style_Value() + { + var target = new Class1(); + var source = new Subject>(); + var property = Class1.FooProperty; + + target.SetValue(property, "style", BindingPriority.Style); + target.SetValue(property, "foo"); + target.Bind(property, source, BindingPriority.Animation); + source.OnNext("bar"); + source.OnCompleted(); + + Assert.Equal("foo", target.GetValue(property)); + } + [Fact] public void Completing_LocalValue_Binding_Raises_PropertyChanged() { diff --git a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs index ed122e4ddc..91b0f9cdee 100644 --- a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs +++ b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs @@ -97,6 +97,27 @@ namespace Avalonia.Base.UnitTests.PropertyStore Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor); } + [Fact] + public void Child_Notifies_About_Setting_Back_To_Default_Value() + { + var parent = new Class1(); + var child = new Class1(); + + parent.Foo = "changed"; + child.Parent = parent; + + bool raised = false; + child.PropertyChanged += (_, args) => + { + raised = args.Property == Class1.FooProperty && args.GetNewValue() == "foodefault"; + }; + + Assert.Equal("changed", child.Foo); // inherited from parent. + + child.Foo = "foodefault"; // reset back to default. + Assert.True(raised); // expect event to be raised, as actual value was changed. + } + [Fact] public void Adding_Child_Sets_InheritanceAncestor() {