diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs index 747ee1c082..f47738f2e4 100644 --- a/src/Avalonia.Base/Layout/LayoutManager.cs +++ b/src/Avalonia.Base/Layout/LayoutManager.cs @@ -249,10 +249,12 @@ namespace Avalonia.Layout { var control = _toMeasure.Dequeue(); - if (!control.IsMeasureValid && control.IsAttachedToVisualTree) + if (!control.IsMeasureValid) { Measure(control); } + + _toArrange.Enqueue(control); } } @@ -262,7 +264,7 @@ namespace Avalonia.Layout { var control = _toArrange.Dequeue(); - if (!control.IsArrangeValid && control.IsAttachedToVisualTree) + if (!control.IsArrangeValid) { Arrange(control); } @@ -297,8 +299,6 @@ namespace Avalonia.Layout { control.Measure(control.PreviousMeasure.Value); } - - _toArrange.Enqueue(control); } return true; diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs index 09f78c5a6c..45a6efdd4a 100644 --- a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs @@ -514,5 +514,38 @@ namespace Avalonia.Base.UnitTests.Layout Assert.True(parent.IsMeasureValid); Assert.True(parent.IsArrangeValid); } + + [Fact] + public void Grandparent_Can_Invalidate_Root_Measure_During_Arrange() + { + // Issue #11161. + var child = new LayoutTestControl(); + var parent = new LayoutTestControl { Child = child }; + var grandparent = new LayoutTestControl { Child = parent }; + var root = new LayoutTestRoot { Child = grandparent }; + + root.LayoutManager.ExecuteInitialLayoutPass(); + + grandparent.DoArrangeOverride = (_, s) => + { + root.InvalidateMeasure(); + return s; + }; + grandparent.CallBaseArrange = true; + + child.InvalidateMeasure(); + grandparent.InvalidateMeasure(); + + root.LayoutManager.ExecuteLayoutPass(); + + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + Assert.True(parent.IsMeasureValid); + Assert.True(parent.IsArrangeValid); + Assert.True(grandparent.IsMeasureValid); + Assert.True(grandparent.IsArrangeValid); + Assert.True(root.IsMeasureValid); + Assert.True(root.IsArrangeValid); + } } } diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs index 62de81006e..d85c7ed9bc 100644 --- a/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs @@ -10,21 +10,41 @@ namespace Avalonia.Base.UnitTests.Layout public bool Arranged { get; set; } public Func DoMeasureOverride { get; set; } public Func DoArrangeOverride { get; set; } + public bool CallBaseMeasure { get; set; } + public bool CallBaseArrange { get; set; } protected override Size MeasureOverride(Size availableSize) { Measured = true; - return DoMeasureOverride != null ? - DoMeasureOverride(this, availableSize) : - base.MeasureOverride(availableSize); + + if (DoMeasureOverride is not null) + { + var overrideResult = DoMeasureOverride(this, availableSize); + return CallBaseMeasure ? + base.MeasureOverride(overrideResult) : + overrideResult; + } + else + { + return base.MeasureOverride(availableSize); + } } protected override Size ArrangeOverride(Size finalSize) { Arranged = true; - return DoArrangeOverride != null ? - DoArrangeOverride(this, finalSize) : - base.ArrangeOverride(finalSize); + + if (DoArrangeOverride is not null) + { + var overrideResult = DoArrangeOverride(this, finalSize); + return CallBaseArrange ? + base.ArrangeOverride(overrideResult) : + overrideResult; + } + else + { + return base.ArrangeOverride(finalSize); + } } } }