Browse Source

Don't use ResourcesChanged to propagate.

Instead of each control listening to its parent `ResourcesChanged` event, use the logical tree to propagate `ResourcesChanged` events:

- When attaching to the logical tree, piggyback on the `AttachedToLogicalTree` traversal
- At other times, propagate by calling `NotifyResourcesChanged`
pull/3957/head
Steven Kirk 6 years ago
parent
commit
eec2a05a26
  1. 63
      src/Avalonia.Styling/StyledElement.cs
  2. 43
      tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs

63
src/Avalonia.Styling/StyledElement.cs

@ -205,19 +205,7 @@ namespace Avalonia
/// each styled element may in addition define its own styles which are applied to the styled element
/// itself and its children.
/// </remarks>
public Styles Styles
{
get
{
if (_styles is null)
{
_styles = new Styles(this);
NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
return _styles;
}
}
public Styles Styles => _styles ??= new Styles(this);
/// <summary>
/// Gets or sets the styled element's resource dictionary.
@ -423,18 +411,6 @@ namespace Avalonia
OnDetachedFromLogicalTreeCore(e);
}
if (old != null)
{
old.ResourcesChanged -= ThisResourcesChanged;
}
if (Parent != null)
{
Parent.ResourcesChanged += ThisResourcesChanged;
}
NotifyResourcesChanged(new ResourcesChangedEventArgs());
var newRoot = FindLogicalRoot(this);
if (newRoot is object)
@ -442,6 +418,14 @@ namespace Avalonia
var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent);
OnAttachedToLogicalTreeCore(e);
}
else
{
// If we were attached to the logical tree, we piggyback on the tree traversal
// there to raise resources changed notifications. If we're being removed from
// the logical tree or attached to a control which is not rooted, then traverse
// the tree raising notifications now.
NotifyResourcesChanged();
}
#nullable disable
RaisePropertyChanged(
@ -638,6 +622,7 @@ namespace Avalonia
_logicalRoot = e.Root;
ApplyStyling();
NotifyResourcesChanged(propagate: false);
OnAttachedToLogicalTree(e);
AttachedToLogicalTree?.Invoke(this, e);
@ -786,14 +771,30 @@ namespace Avalonia
}
}
private void NotifyResourcesChanged(ResourcesChangedEventArgs? e = null)
private void NotifyResourcesChanged(
ResourcesChangedEventArgs? e = null,
bool propagate = true)
{
ResourcesChanged?.Invoke(this, e ?? new ResourcesChangedEventArgs());
}
if (ResourcesChanged is object)
{
e ??= new ResourcesChangedEventArgs();
ResourcesChanged(this, e);
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
NotifyResourcesChanged(e);
if (propagate && _logicalChildren is object)
{
var count = _logicalChildren.Count;
if (count > 0)
{
e ??= new ResourcesChangedEventArgs();
for (var i = 0; i < count; ++i)
{
_logicalChildren[i].NotifyResourcesChanged(e);
}
}
}
}
private static IReadOnlyList<IStyle> RecurseStyles(IReadOnlyList<IStyle> styles)

43
tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs

@ -154,21 +154,42 @@ namespace Avalonia.Controls.UnitTests
};
var raisedOnTarget = false;
var raisedOnPresenter = false;
var raisedOnChild = false;
target.Measure(Size.Infinity);
target.ResourcesChanged += (_, __) => raisedOnTarget = true;
target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true;
child.ResourcesChanged += (_, __) => raisedOnChild = true;
target.Resources.Add("foo", "bar");
Assert.True(raisedOnTarget);
Assert.True(raisedOnPresenter);
Assert.True(raisedOnChild);
}
[Fact]
public void Adding_Resource_Should_Call_Not_Raise_ResourceChanged_On_Template_Children()
{
Border child;
var target = new ContentControl
{
Content = child = new Border(),
Template = ContentControlTemplate(),
};
var raisedOnTarget = false;
var raisedOnPresenter = false;
target.Measure(Size.Infinity);
target.ResourcesChanged += (_, __) => raisedOnTarget = true;
target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true;
target.Resources.Add("foo", "bar");
Assert.True(raisedOnTarget);
Assert.False(raisedOnPresenter);
}
[Fact]
public void Adding_Resource_To_Styles_Should_Raise_ResourceChanged()
{
@ -201,22 +222,6 @@ namespace Avalonia.Controls.UnitTests
Assert.True(raised);
}
[Fact]
public void Setting_Logical_Parent_Raises_Child_ResourcesChanged()
{
var parent = new ContentControl();
var child = new StyledElement();
((ISetLogicalParent)child).SetParent(parent);
var raisedOnChild = false;
child.ResourcesChanged += (_, __) => raisedOnChild = true;
parent.Resources.Add("foo", "bar");
Assert.True(raisedOnChild);
}
private IControlTemplate ContentControlTemplate()
{
return new FuncControlTemplate<ContentControl>((x, scope) =>

Loading…
Cancel
Save