Open squidink7 opened 1 year ago
Another possibly better solution would be to have the ability to create an animation which runs before the control is removed from the visual tree, as this would mean such animations could be created without altering the logic of the control
The desired behavior is possible, albeit not obvious.
My use case for this feature is to play a fade-out animation when closing a window. In WPF, this is possible using the Timeline.Completed
event. Storyboard
(the animation object in WPF) inherits from Timeline
so it is possible to subscribe to the event to get notified when an animation finishes playing. The animation can even be a resource so it can be themed.
In Avalonia, I have acheived the same effect with a slightly different method. If the animation is a resource, it can be run manually from the code-behind, which returns a task that completes when the animation finishes playing.
In case it is useful, here is what I came up with so far (using Avalonia.Xaml.Interactivity
)
Note: This code has not been tested extensively
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Threading;
using Avalonia.Xaml.Interactivity;
using System.Threading.Tasks;
namespace TestUI.Helpers;
public class CloseAnimationBehavior : Behavior<Window>
{
protected override void OnAttached()
{
AssociatedObject.Closing += AssociatedObject_Closing;
base.OnAttached();
}
private void AssociatedObject_Closing(object sender, WindowClosingEventArgs e)
{
// If the animation exists, cancel the event and run it to completion before closing.
if (AssociatedObject.TryFindResource("CloseAnimation", out var resObj) && resObj is Animation anim)
{
// Unsubscribe from the event so this logic only happens once
AssociatedObject.Closing -= AssociatedObject_Closing;
// Cancel the event so the window doesn't close
e.Cancel = true;
// Start the animation on the current (UI) thread
var animTask = anim.RunAsync(AssociatedObject);
// Wait for it on a different thread
Task.Run(async () =>
{
// Wait for the animation to complete
await animTask;
// Close the window from the UI thread
Dispatcher.UIThread.Invoke(() => AssociatedObject.Close());
});
}
}
}
My style:
...
<!-- Window style -->
<Style Selector="Window.main">
<Style.Animations>
<Animation Duration="0:0:0.7" IterationCount="1">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0.0" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1.0" />
</KeyFrame>
</Animation>
</Style.Animations>
<Style.Resources>
<Animation x:Key="CloseAnimation" Duration="0:0:0.7" IterationCount="1">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="1.0" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="0.0" />
</KeyFrame>
</Animation>
</Style.Resources>
</Style>
...
Relevant parts of the window:
<Window xmlns="https://github.com/avaloniaui"
xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
xmlns:helpers="clr-namespace:TestUI.Helpers;assembly=TestUI"
Classes="main">
<i:Interaction.Behaviors>
<helpers:CloseAnimationBehavior />
</i:Interaction.Behaviors>
...
</Window>
Why do you run your animation in a different thread? It's potentially dangerous.
I am only waiting for it to complete from a different thread.
On February 28, 2024 3:15:50 AM GMT+02:00, Max Katz @.***> wrote:
Why do you run your animation in a different thread? It's potentially dangerous.
-- Reply to this email directly or view it on GitHub: https://github.com/AvaloniaUI/Avalonia/issues/9771#issuecomment-1968008846 You are receiving this because you commented.
Message ID: @.***> -- Sent from my Android device with K-9 Mail. Please excuse my brevity.
Is your feature request related to a problem? Please describe. I am trying to create animations for the process of removing controls both in list views but also custom controls such as dialogs and application pages. After the animation completes I would like to delete this control.
Describe the solution you'd like Ideally, I would like either a way to specify keyframes in my animation that execute methods as opposed to traditional setters, or an event that is emitted when an animation completes.
Describe alternatives you've considered I have tried to define the animation in code-behind, however right now the process for creating custom animations is rather cumbersome when done outside of xaml.
Additional context Avalonia is my first introduction to xaml (I've never used WPF), so my approach may be completely wrong. I'm currently going off experience in other frameworks such as CSS/JS and Qt, so if this method of animation is not how xaml frameworks typically work please let me know.