diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs
index b8a45e102f..d47a7a7809 100644
--- a/src/Avalonia.Controls/ContentControl.cs
+++ b/src/Avalonia.Controls/ContentControl.cs
@@ -116,14 +116,19 @@ namespace Avalonia.Controls
return false;
}
- private void ContentChanged(AvaloniaPropertyChangedEventArgs e)
+ protected virtual void ContentChanged(AvaloniaPropertyChangedEventArgs e)
{
- if (e.OldValue is ILogical oldChild)
+ UpdateLogicalTree(e.OldValue, e.NewValue);
+ }
+
+ protected void UpdateLogicalTree(object? toRemove, object? toAdd)
+ {
+ if (toRemove is ILogical oldChild)
{
LogicalChildren.Remove(oldChild);
}
- if (e.NewValue is ILogical newChild)
+ if (toAdd is ILogical newChild)
{
LogicalChildren.Add(newChild);
}
diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs
index 70b21b7248..545032befb 100644
--- a/src/Avalonia.Controls/TransitioningContentControl.cs
+++ b/src/Avalonia.Controls/TransitioningContentControl.cs
@@ -71,6 +71,11 @@ public class TransitioningContentControl : ContentControl
}
}
+ protected override void ContentChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ // We do nothing becuse we should not remove old Content until the animation is over
+ }
+
///
/// Updates the content with transitions.
///
@@ -89,6 +94,8 @@ public class TransitioningContentControl : ContentControl
if (PageTransition != null)
await PageTransition.Start(this, null, true, localToken);
+ UpdateLogicalTree(CurrentContent, content);
+
if (localToken.IsCancellationRequested)
{
return;
diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs
new file mode 100644
index 0000000000..fa523d7f78
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs
@@ -0,0 +1,63 @@
+using System;
+using Avalonia.LogicalTree;
+using Avalonia.UnitTests;
+using Xunit;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Animation;
+
+namespace Avalonia.Controls.UnitTests
+{
+ public class TransitioningContentControlTests
+ {
+ [Fact]
+ public void Old_Content_Shuold_Be_Removed__From_Logical_Tree_After_Out_Animation()
+ {
+ var testTransition = new TestTransition();
+
+ var target = new TransitioningContentControl();
+ target.PageTransition = testTransition;
+
+ var root = new TestRoot() { Child = target };
+
+ var oldControl = new Control();
+ var newControl = new Control();
+
+ target.Content = oldControl;
+ Threading.Dispatcher.UIThread.RunJobs();
+
+ Assert.Equal(target, oldControl.GetLogicalParent());
+ Assert.Equal(null, newControl.GetLogicalParent());
+
+ testTransition.BeginTransition += isFrom =>
+ {
+ // Old out
+ if (isFrom)
+ {
+ Assert.Equal(target, oldControl.GetLogicalParent());
+ Assert.Equal(null, newControl.GetLogicalParent());
+ }
+ // New in
+ else
+ {
+ Assert.Equal(null, oldControl.GetLogicalParent());
+ Assert.Equal(target, newControl.GetLogicalParent());
+ }
+ };
+
+ target.Content = newControl;
+ Threading.Dispatcher.UIThread.RunJobs();
+ }
+ }
+ public class TestTransition : IPageTransition
+ {
+ public event Action BeginTransition;
+
+ public Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken)
+ {
+ bool isFrom = from != null && to == null;
+ BeginTransition?.Invoke(isFrom);
+ return Task.CompletedTask;
+ }
+ }
+}