Browse Source

Fix a couple of inheritance bugs.

With associated unit tests.
refactor/style-priorities
Steven Kirk 4 years ago
parent
commit
0119cf1af1
  1. 18
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  2. 42
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  3. 47
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs
  4. 17
      tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs

18
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -257,11 +257,23 @@ namespace Avalonia.PropertyStore
public void DisposeAndRaiseUnset(ValueStore owner, StyledPropertyBase<T> property)
{
var defaultValue = property.GetDefaultValue(owner.GetType());
BindingPriority priority;
T oldValue;
if (!EqualityComparer<T>.Default.Equals(defaultValue, Value))
if (property.Inherits && owner.TryGetInheritedValue(property, out var i))
{
owner.Owner.RaisePropertyChanged(property, Value, defaultValue, BindingPriority.Unset, true);
oldValue = ((EffectiveValue<T>)i).Value;
priority = BindingPriority.Inherited;
}
else
{
oldValue = property.GetDefaultValue(owner.GetType());
priority = BindingPriority.Unset;
}
if (!EqualityComparer<T>.Default.Equals(oldValue, Value))
{
owner.Owner.RaisePropertyChanged(property, Value, oldValue, priority, true);
if (property.Inherits)
owner.OnInheritedEffectiveValueDisposed(property, Value);
}

42
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -206,6 +206,25 @@ namespace Avalonia.PropertyStore
return default;
}
public bool TryGetInheritedValue(
AvaloniaProperty property,
[NotNullWhen(true)] out EffectiveValue? result)
{
Debug.Assert(property.Inherits);
var i = InheritanceAncestor;
while (i is not null)
{
if (i.TryGetEffectiveValue(property, out result))
return true;
i = i.InheritanceAncestor;
}
result = null;
return false;
}
public void SetInheritanceParent(AvaloniaObject? oldParent, AvaloniaObject? newParent)
{
var values = DictionaryPool<AvaloniaProperty, OldNewValue>.Get();
@ -651,7 +670,7 @@ namespace Avalonia.PropertyStore
if (_effectiveValues is not null && _effectiveValues.Remove(property))
{
if (property.Inherits && --_inheritedValueCount == 0)
OnInheritanceAncestorChanged(InheritanceAncestor?.InheritanceAncestor);
OnInheritanceAncestorChanged(InheritanceAncestor);
return true;
}
@ -663,7 +682,7 @@ namespace Avalonia.PropertyStore
if (_effectiveValues is not null && _effectiveValues.Remove(property, out result))
{
if (property.Inherits && --_inheritedValueCount == 0)
OnInheritanceAncestorChanged(InheritanceAncestor?.InheritanceAncestor);
OnInheritanceAncestorChanged(InheritanceAncestor);
return true;
}
@ -898,25 +917,6 @@ namespace Avalonia.PropertyStore
return false;
}
private bool TryGetInheritedValue(
AvaloniaProperty property,
[NotNullWhen(true)] out EffectiveValue? result)
{
Debug.Assert(property.Inherits);
var i = InheritanceAncestor;
while (i is not null)
{
if (i.TryGetEffectiveValue(property, out result))
return true;
i = i.InheritanceAncestor;
}
result = null;
return false;
}
private EffectiveValue? GetEffectiveValue(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var value))

47
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs

@ -43,6 +43,53 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("bazdefault", child.GetValue(Class1.BazProperty));
}
[Fact]
public void ClearValue_On_Parent_Raises_PropertyChanged_On_Child()
{
Class1 parent = new Class1();
Class2 child = new Class2 { Parent = parent };
var raised = 0;
parent.SetValue(Class1.BazProperty, "changed");
child.PropertyChanged += (s, e) =>
{
Assert.Same(child, e.Sender);
Assert.Equal("changed", e.OldValue);
Assert.Equal("bazdefault", e.NewValue);
Assert.Equal(BindingPriority.Inherited, e.Priority);
++raised;
};
parent.ClearValue(Class1.BazProperty);
Assert.Equal(1, raised);
}
[Fact]
public void ClearValue_On_Child_Raises_PropertyChanged_With_Inherited_Parent_Value()
{
Class1 parent = new Class1();
Class2 child = new Class2 { Parent = parent };
var raised = 0;
parent.SetValue(Class1.BazProperty, "parent");
child.SetValue(Class1.BazProperty, "child");
child.PropertyChanged += (s, e) =>
{
Assert.Same(child, e.Sender);
Assert.Equal("child", e.OldValue);
Assert.Equal("parent", e.NewValue);
Assert.Equal(BindingPriority.Inherited, e.Priority);
++raised;
};
child.ClearValue(Class1.BazProperty);
Assert.Equal(1, raised);
}
[Fact]
public void Setting_InheritanceParent_Raises_PropertyChanged_When_Parent_Has_Value_Set()
{

17
tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs

@ -80,6 +80,23 @@ namespace Avalonia.Base.UnitTests.PropertyStore
Assert.Same(child.GetValueStore(), grandchild.GetValueStore().InheritanceAncestor);
}
[Fact]
public void Clearing_Value_In_Child_Updates_InheritanceAncestor()
{
var parent = new Class1();
var child = new Class1 { Parent = parent };
var grandchild = new Class1 { Parent = child };
parent.Foo = "changed";
child.Foo = "foochanged";
child.ClearValue(Class1.FooProperty);
var parentStore = parent.GetValueStore();
Assert.Null(parentStore.InheritanceAncestor);
Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor);
Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor);
}
[Fact]
public void Adding_Child_Sets_InheritanceAncestor()
{

Loading…
Cancel
Save