Browse Source

Raise the `EffectiveViewPortChanged` when a control is added to the tree that has already has a valid layout (#17570)

* add unit tests to show that the EffectiveViewPortChanged event is not being raised when it should.

note: this tests are verified against UWP behavior.

* fix raising of EffectiveViewPortChanged event.

---------

Co-authored-by: Julien Lebosquain <[email protected]>
Co-authored-by: Max Katz <[email protected]>
Dan Walmsley 10 months ago
parent
commit
f4d28d5d45

+ 3 - 6
src/Avalonia.Base/Layout/LayoutManager.cs

@@ -213,9 +213,7 @@ namespace Avalonia.Layout
         void ILayoutManager.RegisterEffectiveViewportListener(Layoutable control)
         {
             _effectiveViewportChangedListeners ??= new List<EffectiveViewportChangedListener>();
-            _effectiveViewportChangedListeners.Add(new EffectiveViewportChangedListener(
-                control,
-                CalculateEffectiveViewport(control)));
+            _effectiveViewportChangedListeners.Add(new EffectiveViewportChangedListener(control));
         }
 
         void ILayoutManager.UnregisterEffectiveViewportListener(Layoutable control)
@@ -438,14 +436,13 @@ namespace Avalonia.Layout
 
         private class EffectiveViewportChangedListener
         {
-            public EffectiveViewportChangedListener(Layoutable listener, Rect viewport)
+            public EffectiveViewportChangedListener(Layoutable listener)
             {
                 Listener = listener;
-                Viewport = viewport;
             }
 
             public Layoutable Listener { get; }
-            public Rect Viewport { get; set; }
+            public Rect? Viewport { get; set; }
         }
 
         private enum ArrangeResult

+ 59 - 2
tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Base.UnitTests.Layout
     public class LayoutableTests_EffectiveViewportChanged
     {
         [Fact]
-        public async Task EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree()
+        public async Task EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree_And_Layout_Pass_Has_Not_Run()
         {
 #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
             await RunOnUIThread.Execute(async () =>
@@ -34,6 +34,60 @@ namespace Avalonia.Base.UnitTests.Layout
                 Assert.Equal(0, raised);
             });
         }
+        
+        [Fact]
+        public async Task EffectiveViewportChanged_Raised_When_Control_Added_To_Tree_And_Layout_Pass_Has_Run()
+        {
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+            await RunOnUIThread.Execute(async () =>
+#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+            {
+                var root = CreateRoot();
+                var target = new Canvas();
+                var raised = 0;
+
+                target.EffectiveViewportChanged += (s, e) =>
+                {
+                    ++raised;
+                };
+
+                root.Child = target;
+
+                Assert.Equal(0, raised);
+
+                await ExecuteInitialLayoutPass(root);
+
+                Assert.Equal(1, raised);
+            });
+        }
+        
+        [Fact]
+        public async Task EffectiveViewportChanged_Raised_When_Root_LayedOut_And_Then_Control_Added_To_Tree_And_Layout_Pass_Runs()
+        {
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+            await RunOnUIThread.Execute(async () =>
+#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+            {
+                var root = CreateRoot();
+                var target = new Canvas();
+                var raised = 0;
+
+                target.EffectiveViewportChanged += (s, e) =>
+                {
+                    ++raised;
+                };
+
+                await ExecuteInitialLayoutPass(root);
+
+                root.Child = target;
+
+                Assert.Equal(0, raised);
+
+                await ExecuteInitialLayoutPass(root);
+
+                Assert.Equal(1, raised);
+            });
+        }
 
         [Fact]
         public async Task EffectiveViewportChanged_Raised_Before_LayoutUpdated()
@@ -268,8 +322,11 @@ namespace Avalonia.Base.UnitTests.Layout
 
                 root.Child = parent;
 
-                await ExecuteInitialLayoutPass(root);
                 target.EffectiveViewportChanged += (s, e) => ++raised;
+                await ExecuteInitialLayoutPass(root);
+                
+                raised = 0; // The initial layout pass is expected to raise.
+                
                 target.RenderTransform = new TranslateTransform { X = 8 };
                 target.InvalidateMeasure();
                 await ExecuteLayoutPass(root);