diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs
new file mode 100644
index 0000000000..2a6f3620e5
--- /dev/null
+++ b/src/Avalonia.Controls/TransitioningContentControl.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Threading;
+using Avalonia.Animation;
+using Avalonia.Controls.Templates;
+using Avalonia.Threading;
+
+namespace Avalonia.Controls;
+
+///
+/// Displays according to a .
+/// Uses to move between the old and new content values.
+///
+public class TransitioningContentControl : ContentControl
+{
+ private CancellationTokenSource? _lastTransitionCts;
+ private object? _currentContent;
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PageTransitionProperty =
+ AvaloniaProperty.Register(nameof(PageTransition),
+ new CrossFade(TimeSpan.FromSeconds(0.125)));
+
+ ///
+ /// Defines the property.
+ ///
+ internal static readonly DirectProperty CurrentContentProperty =
+ AvaloniaProperty.RegisterDirect(nameof(CurrentContent),
+ o => o.CurrentContent);
+
+ ///
+ /// Gets or sets the animation played when content appears and disappears.
+ ///
+ public IPageTransition? PageTransition
+ {
+ get => GetValue(PageTransitionProperty);
+ set => SetValue(PageTransitionProperty, value);
+ }
+
+ ///
+ /// Gets the content currently displayed on the screen.
+ ///
+ internal object? CurrentContent
+ {
+ get => _currentContent;
+ private set => SetAndRaise(CurrentContentProperty, ref _currentContent, value);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _lastTransitionCts?.Cancel();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == ContentProperty)
+ {
+ Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
+ }
+ }
+
+ ///
+ /// Updates the content with transitions.
+ ///
+ /// New content to set.
+ private async void UpdateContentWithTransition(object? content)
+ {
+ if (VisualRoot is null)
+ {
+ return;
+ }
+
+ _lastTransitionCts?.Cancel();
+ _lastTransitionCts = new CancellationTokenSource();
+
+ if (PageTransition != null)
+ await PageTransition.Start(this, null, true, _lastTransitionCts.Token);
+
+ CurrentContent = content;
+
+ if (PageTransition != null)
+ await PageTransition.Start(null, this, true, _lastTransitionCts.Token);
+ }
+}
diff --git a/src/Avalonia.ReactiveUI/TransitioningContentControl.cs b/src/Avalonia.ReactiveUI/TransitioningContentControl.cs
index c4dd79f468..d26e90b2da 100644
--- a/src/Avalonia.ReactiveUI/TransitioningContentControl.cs
+++ b/src/Avalonia.ReactiveUI/TransitioningContentControl.cs
@@ -10,6 +10,7 @@ namespace Avalonia.ReactiveUI
///
/// A ContentControl that animates the transition when its content is changed.
///
+ [Obsolete("Use TransitioningContentControl in Avalonia.Controls namespace")]
public class TransitioningContentControl : ContentControl, IStyleable
{
///
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml
index 4ae9ea4812..5f548435fc 100644
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml
+++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml
@@ -38,6 +38,7 @@
+
diff --git a/src/Avalonia.Themes.Default/TransitioningContentControl.xaml b/src/Avalonia.Themes.Default/TransitioningContentControl.xaml
new file mode 100644
index 0000000000..a1bd660527
--- /dev/null
+++ b/src/Avalonia.Themes.Default/TransitioningContentControl.xaml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
index 16e05ffdfd..4d62f8bcff 100644
--- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
@@ -37,6 +37,7 @@
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml
new file mode 100644
index 0000000000..a1bd660527
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml
@@ -0,0 +1,20 @@
+
+
+