Browse Source

Fix LayoutUpdated and EffectiveViewportChanged double registration (#17196)

* Add failing test for LayoutUpdated and EffectiveViewportChanged

* Fix LayoutUpdated and EffectiveViewportChanged registration
release/11.2.0-rc2
Julien Lebosquain 2 years ago
committed by Max Katz
parent
commit
ff1148c843
  1. 16
      src/Avalonia.Base/Layout/Layoutable.cs
  2. 24
      tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs
  3. 22
      tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs

16
src/Avalonia.Base/Layout/Layoutable.cs

@ -135,6 +135,7 @@ namespace Avalonia.Layout
private Rect? _previousArrange; private Rect? _previousArrange;
private EventHandler<EffectiveViewportChangedEventArgs>? _effectiveViewportChanged; private EventHandler<EffectiveViewportChangedEventArgs>? _effectiveViewportChanged;
private EventHandler? _layoutUpdated; private EventHandler? _layoutUpdated;
private bool _isAttachingToVisualTree;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Layoutable"/> class. /// Initializes static members of the <see cref="Layoutable"/> class.
@ -164,7 +165,7 @@ namespace Avalonia.Layout
{ {
add add
{ {
if (_effectiveViewportChanged is null && VisualRoot is ILayoutRoot r) if (_effectiveViewportChanged is null && VisualRoot is ILayoutRoot r && !_isAttachingToVisualTree)
{ {
r.LayoutManager.RegisterEffectiveViewportListener(this); r.LayoutManager.RegisterEffectiveViewportListener(this);
} }
@ -190,7 +191,7 @@ namespace Avalonia.Layout
{ {
add add
{ {
if (_layoutUpdated is null && VisualRoot is ILayoutRoot r) if (_layoutUpdated is null && VisualRoot is ILayoutRoot r && !_isAttachingToVisualTree)
{ {
r.LayoutManager.LayoutUpdated += LayoutManagedLayoutUpdated; r.LayoutManager.LayoutUpdated += LayoutManagedLayoutUpdated;
} }
@ -735,9 +736,18 @@ namespace Avalonia.Layout
InvalidateMeasure(); InvalidateMeasure();
} }
/// <inheritdoc />
protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTreeCore(e); _isAttachingToVisualTree = true;
try
{
base.OnAttachedToVisualTreeCore(e);
}
finally
{
_isAttachingToVisualTree = false;
}
if (e.Root is ILayoutRoot r) if (e.Root is ILayoutRoot r)
{ {

24
tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs

@ -291,6 +291,30 @@ namespace Avalonia.Base.UnitTests.Layout
Times.Once); Times.Once);
} }
[Fact]
public void LayoutManager_LayoutUpdated_Should_Not_Be_Subscribed_Twice_In_AttachedToVisualTree()
{
Border border1;
var layoutManager = new Mock<ILayoutManager>();
layoutManager.SetupAdd(m => m.LayoutUpdated += (_, _) => { });
_ = new TestRoot
{
Child = border1 = new Border(),
LayoutManager = layoutManager.Object,
};
var border2 = new Border();
border2.AttachedToVisualTree += (_, _) => border2.LayoutUpdated += (_, _) => { };
layoutManager.Invocations.Clear();
border1.Child = border2;
layoutManager.VerifyAdd(
x => x.LayoutUpdated += It.IsAny<EventHandler>(),
Times.Once);
}
[Fact] [Fact]
public void Making_Control_Invisible_Should_Invalidate_Parent_Measure() public void Making_Control_Invisible_Should_Invalidate_Parent_Measure()
{ {

22
tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs

@ -57,6 +57,28 @@ namespace Avalonia.Base.UnitTests.Layout
}); });
} }
[Fact]
public async Task EffectiveViewportChanged_Should_Not_Be_Raised_Twice_If_Subcribed_In_AttachedToVisualTree()
{
await RunOnUIThread.Execute(async () =>
{
var root = CreateRoot();
var target = new Canvas();
var raised = 0;
target.AttachedToVisualTree += (_, _) =>
{
target.EffectiveViewportChanged += (_, _) => ++raised;
};
root.Child = target;
await ExecuteInitialLayoutPass(root);
Assert.Equal(1, raised);
});
}
[Fact] [Fact]
public async Task Parent_Affects_EffectiveViewport() public async Task Parent_Affects_EffectiveViewport()
{ {

Loading…
Cancel
Save