From 9318ce1612579cb5d1f7b36c33012137f955e9ac Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 7 Feb 2022 16:28:40 +0100 Subject: [PATCH 1/5] Added failing tests for #7552. --- .../LayoutableTests.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index a8aa0bbf0e..514eae12c0 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -320,6 +320,71 @@ namespace Avalonia.Layout.UnitTests Times.Once); } + [Fact] + public void Making_Control_Invisible_Should_Invalidate_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + + child.IsVisible = false; + + Assert.False(target.IsMeasureValid); + Assert.False(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + } + + [Fact] + public void Measuring_Invisible_Control_Should_Not_Invalidate_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.Equal(new Size(100, 0), child.DesiredSize); + + child.IsVisible = false; + Assert.Equal(default, child.DesiredSize); + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.Equal(default, child.DesiredSize); + } + private class TestLayoutable : Layoutable { public Size ArrangeSize { get; private set; } From 1f3cb4fa00dfc6e443d92a1658517653c039066b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 7 Feb 2022 16:29:39 +0100 Subject: [PATCH 2/5] Invalidate parent measure when child visibility changes. Fixes #7552 but breaks `ListBoxTests.LayoutManager_Should_Measure_Arrange_All`. --- src/Avalonia.Layout/Layoutable.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 09e0c4263a..516a70c3c9 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -141,7 +141,6 @@ namespace Avalonia.Layout static Layoutable() { AffectsMeasure( - IsVisibleProperty, WidthProperty, HeightProperty, MinWidthProperty, @@ -781,6 +780,17 @@ namespace Avalonia.Layout { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsVisibleProperty) + { + DesiredSize = default; + this.GetVisualParent()?.ChildDesiredSizeChanged(this); + } + } + /// protected sealed override void OnVisualParentChanged(IVisual? oldParent, IVisual? newParent) { From 57c6db613ebb98a57eb6b2e55cfe48db68f9a2b0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 17 May 2022 12:21:12 +0200 Subject: [PATCH 3/5] Update OnPropertyChanged after merge. --- src/Avalonia.Base/Layout/Layoutable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs index 97ef9b8f7a..f425c04a71 100644 --- a/src/Avalonia.Base/Layout/Layoutable.cs +++ b/src/Avalonia.Base/Layout/Layoutable.cs @@ -790,7 +790,7 @@ namespace Avalonia.Layout { } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); From ddf266d9ae6bb9220e7b53a2ec63215f2c8b62a8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 May 2022 13:20:08 +0200 Subject: [PATCH 4/5] Invalidate measure when showing a control. But not when hiding it. --- src/Avalonia.Base/Layout/Layoutable.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs index f425c04a71..f30925f489 100644 --- a/src/Avalonia.Base/Layout/Layoutable.cs +++ b/src/Avalonia.Base/Layout/Layoutable.cs @@ -797,7 +797,15 @@ namespace Avalonia.Layout if (change.Property == IsVisibleProperty) { DesiredSize = default; + + // All changes to visibility cause the parent element to be notified. this.GetVisualParent()?.ChildDesiredSizeChanged(this); + + // We only invalidate outselves when visibility is changed to true. + if (change.GetNewValue()) + { + InvalidateMeasure(); + } } } From 9f03a1f7be2c9c645064732188546a417f5f65be Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 May 2022 13:26:48 +0200 Subject: [PATCH 5/5] Added additional test for visibility invalidation. --- .../Layout/LayoutableTests.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs index 6faf3e458e..184aada6cd 100644 --- a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs @@ -352,6 +352,38 @@ namespace Avalonia.Base.UnitTests.Layout Assert.True(child.IsArrangeValid); } + [Fact] + public void Making_Control_Visible_Should_Invalidate_Own_And_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + IsVisible = false, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.False(child.IsArrangeValid); + + child.IsVisible = true; + + Assert.False(target.IsMeasureValid); + Assert.False(target.IsArrangeValid); + Assert.False(child.IsMeasureValid); + Assert.False(child.IsArrangeValid); + } + [Fact] public void Measuring_Invisible_Control_Should_Not_Invalidate_Parent_Measure() {