Browse Source

Don't expose viewbox container as logical child.

#7735 introduced an internal container control which hosts the child, but it exposed this child in the logical tree, breaking any styles which relied on the `Viewbox.Child` being the logical child of the `Viewbox`.

Fix this by introducing a simple internal `ViewboxContainer` control as the container.
Steven Kirk 3 years ago
parent
commit
35db70c8d4
2 changed files with 60 additions and 3 deletions
  1. 40 3
      src/Avalonia.Controls/Viewbox.cs
  2. 20 0
      tests/Avalonia.Controls.UnitTests/ViewboxTests.cs

+ 40 - 3
src/Avalonia.Controls/Viewbox.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Controls
     /// </summary>
     public class Viewbox : Control
     {
-        private Decorator _containerVisual;
+        private ViewboxContainer _containerVisual;
 
         /// <summary>
         /// Defines the <see cref="Stretch"/> property.
@@ -37,9 +37,8 @@ namespace Avalonia.Controls
 
         public Viewbox()
         {
-            _containerVisual = new Decorator();
+            _containerVisual = new ViewboxContainer();
             _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft;
-            LogicalChildren.Add(_containerVisual);
             VisualChildren.Add(_containerVisual);
         }
 
@@ -88,7 +87,22 @@ namespace Avalonia.Controls
 
             if (change.Property == ChildProperty)
             {
+                var (oldChild, newChild) = change.GetOldAndNewValue<IControl>();
+
+                if (oldChild is not null)
+                {
+                    ((ISetLogicalParent)oldChild).SetParent(null);
+                    LogicalChildren.Remove(oldChild);
+                }
+
                 _containerVisual.Child = change.GetNewValue<IControl>();
+
+                if (newChild is not null)
+                {
+                    ((ISetLogicalParent)newChild).SetParent(this);
+                    LogicalChildren.Add(newChild);
+                }
+
                 InvalidateMeasure();
             }
         }
@@ -129,5 +143,28 @@ namespace Avalonia.Controls
 
             return finalSize;
         }
+
+        private class ViewboxContainer : Control
+        {
+            private IControl? _child;
+
+            public IControl? Child
+            {
+                get => _child;
+                set
+                {
+                    if (_child != value)
+                    {
+                        if (_child is not null)
+                            VisualChildren.Remove(_child);
+
+                        _child = value;
+
+                        if (_child is not null)
+                            VisualChildren.Add(_child);
+                    }
+                }
+            }
+        }
     }
 }

+ 20 - 0
tests/Avalonia.Controls.UnitTests/ViewboxTests.cs

@@ -1,4 +1,5 @@
 using Avalonia.Controls.Shapes;
+using Avalonia.LogicalTree;
 using Avalonia.Media;
 using Avalonia.UnitTests;
 using Xunit;
@@ -170,5 +171,24 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(expectedScale, scaleTransform.ScaleX);
             Assert.Equal(expectedScale, scaleTransform.ScaleY);
         }
+
+        [Fact]
+        public void Child_Should_Be_Logical_Child_Of_Viewbox()
+        {
+            var target = new Viewbox();
+
+            Assert.Empty(target.GetLogicalChildren());
+
+            var child = new Canvas();
+            target.Child = child;
+
+            Assert.Single(target.GetLogicalChildren(), child);
+            Assert.Same(child.GetLogicalParent(), target);
+
+            target.Child = null;
+
+            Assert.Empty(target.GetLogicalChildren());
+            Assert.Null(child.GetLogicalParent());
+        }
     }
 }