From ccb8aa0daeb5d6a67f8c7321c57e84a5c334041d Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Wed, 16 Oct 2019 13:36:42 +0100 Subject: [PATCH] Merge pull request #3119 from AvaloniaUI/fixes/3095-visualnode-wrong-parent Ensure VisualNode has correct parent. --- .../Rendering/SceneGraph/SceneBuilder.cs | 15 ++++++++++++++- .../Rendering/SceneGraph/VisualNode.cs | 10 ++++++++++ .../SceneGraph/DeferredDrawingContextImplTests.cs | 12 ++++++------ .../Rendering/SceneGraph/SceneBuilderTests.cs | 3 +-- .../Rendering/SceneGraph/VisualNodeTests.cs | 8 ++++---- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 87c9ed0bae..558e96e132 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -148,6 +148,19 @@ namespace Avalonia.Rendering.SceneGraph return (VisualNode)node; } + private static object GetOrCreateChildNode(Scene scene, IVisual child, VisualNode parent) + { + var result = (VisualNode)scene.FindNode(child); + + if (result != null && result.Parent != parent) + { + Deindex(scene, result); + result = null; + } + + return result ?? CreateNode(scene, child, parent); + } + private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse) { var visual = node.Visual; @@ -231,7 +244,7 @@ namespace Avalonia.Rendering.SceneGraph { foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) { - var childNode = scene.FindNode(child) ?? CreateNode(scene, child, node); + var childNode = GetOrCreateChildNode(scene, child, node); Update(context, scene, (VisualNode)childNode, clip, forceRecurse); } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index d342f2eb2e..d2a9e0a673 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -119,6 +119,11 @@ namespace Avalonia.Rendering.SceneGraph throw new ObjectDisposedException("Visual node for {node.Visual}"); } + if (child.Parent != this) + { + throw new AvaloniaInternalException("VisualNode added to wrong parent."); + } + EnsureChildrenCreated(); _children.Add(child); } @@ -155,6 +160,11 @@ namespace Avalonia.Rendering.SceneGraph throw new ObjectDisposedException("Visual node for {node.Visual}"); } + if (node.Parent != this) + { + throw new AvaloniaInternalException("VisualNode added to wrong parent."); + } + EnsureChildrenCreated(); _children[index] = node; } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs index 4d23c57eed..f57c73c45c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs @@ -17,7 +17,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Add_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child = new VisualNode(Mock.Of(), null); + var child = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); var target = new DeferredDrawingContextImpl(null, layers); @@ -32,7 +32,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Not_Replace_Identical_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child = new VisualNode(Mock.Of(), null); + var child = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); parent.AddChild(child); @@ -50,8 +50,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Replace_Different_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child1 = new VisualNode(Mock.Of(), null); - var child2 = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), parent); + var child2 = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); parent.AddChild(child1); @@ -78,8 +78,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var layers = new SceneLayers(root); var target = new DeferredDrawingContextImpl(null, layers); - var child1 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; - var child2 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; + var child1 = new VisualNode(Mock.Of(), node) { LayerRoot = root }; + var child2 = new VisualNode(Mock.Of(), node) { LayerRoot = root }; target.BeginUpdate(node); using (target.BeginUpdate(child1)) { } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index 5fd14e9ea9..327ef98c4d 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -620,10 +620,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph border1.IsVisible = false; scene = scene.CloneScene(); - - var panelNode = (VisualNode)scene.FindNode(panel); sceneBuilder.Update(scene, decorator); + var panelNode = (VisualNode)scene.FindNode(panel); Assert.Equal(2, panelNode.Children.Count); Assert.False(panelNode.Children[0].Disposed); Assert.False(panelNode.Children[1].Disposed); diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index 3211c3397b..4ec3630053 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -24,7 +24,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(Mock.Of(), null); var collection = node.Children; - node.AddChild(Mock.Of()); + node.AddChild(Mock.Of(x => x.Parent == node)); Assert.NotSame(collection, node.Children); } @@ -106,9 +106,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void TrimChildren_Should_Work_Correctly() { var parent = new VisualNode(Mock.Of(), null); - var child1 = new VisualNode(Mock.Of(), null); - var child2 = new VisualNode(Mock.Of(), null); - var child3 = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), parent); + var child2 = new VisualNode(Mock.Of(), parent); + var child3 = new VisualNode(Mock.Of(), parent); parent.AddChild(child1); parent.AddChild(child2);