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 @@ + + +