Fading out a wpf window on close

拜拜、爱过 提交于 2019-11-28 19:22:54

Closing is not a routed event, so you can't use it in an EventTrigger. Perhaps you could start the storyboard in the handler of the ClosingEvent in the code-behind and cancel the event... something like that :

private bool closeStoryBoardCompleted = false;

private void Window_Closing(object sender, CancelEventArgs e)
{
    if (!closeStoryBoardCompleted)
    {
        closeStoryBoard.Begin();
        e.Cancel = true;
    }
}

private void closeStoryBoard_Completed(object sender, EventArgs e)
{
    closeStoryBoardCompleted = true;
    this.Close();
}

I thought I'd add another solution of doing this, using behaviors from the Expression SDK and combining it with the solution from @Thomas. Using that, we can define a "CloseBehavior" that handles the code behind of starting a storyboard and closing the window when it's done.

using System.ComponentModel;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;

namespace Presentation.Behaviours {
    public class CloseBehavior : Behavior<Window> {
        public static readonly DependencyProperty StoryboardProperty =
            DependencyProperty.Register("Storyboard", typeof(Storyboard), typeof(CloseBehavior), new PropertyMetadata(default(Storyboard)));

        public Storyboard Storyboard {
            get { return (Storyboard)GetValue(StoryboardProperty); }
            set { SetValue(StoryboardProperty, value); }
        }

        protected override void OnAttached() {
            base.OnAttached();
            AssociatedObject.Closing += onWindowClosing;
        }

        private void onWindowClosing(object sender, CancelEventArgs e) {
            if (Storyboard == null) {
                return;
            }
            e.Cancel = true;
            AssociatedObject.Closing -= onWindowClosing;

            Storyboard.Completed += (o, a) => AssociatedObject.Close();
            Storyboard.Begin(AssociatedObject);
        }
    }
}

The behavior defines a storyboard as a dependency property, so we can set it in xaml and when the AssociatedObject (the window where we define the behavior) is closing, this storyboard is started using Storyboard.Begin(). Now, in xaml we simply add the behavior to the window using the following xaml

<Window x:Class="Presentation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:behave="clr-namespace:Presentation.Behaviours"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        x:Name="window">
    <Window.Resources>
        <Storyboard x:Key="ExitAnimation">
            <DoubleAnimation Storyboard.Target="{Binding ElementName='window'}"
                             Storyboard.TargetProperty="(Window.Opacity)"
                             Duration="0:0:1" From="1" To="0"/>
        </Storyboard>
    </Window.Resources>

    <i:Interaction.Behaviors>
        <behave:CloseBehavior Storyboard="{StaticResource ExitAnimation}"/>
    </i:Interaction.Behaviors>

    <Grid>
    </Grid>
</Window>

Note the xml namespace i from the System.Windows.Interactivity dll, and also that the window is referenced, so it has to have a x:Name assigned. Now we simply add the behavior to every window on which we wish to execute a storyboard before closing the application, instead of copying the logic to every code-behind in each window.

I'm not an expert on WPF but I believe that unless you cancel the initial Closing event the window will be gone before the animation is even started.

Upon receiving the Window.Closing event, you should cancel the event and start the animation. When the animation is done you can close the window.

This is even simpler and shorter. Add a behavior as follows:

public class WindowClosingBehavior : Behavior<Window>
    {
        protected override void OnAttached()
        {
            AssociatedObject.Closing += AssociatedObject_Closing;
        }

        private void AssociatedObject_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Window window = sender as Window;
            window.Closing -= AssociatedObject_Closing;
            e.Cancel = true;
            var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.5));
            anim.Completed += (s, _) => window.Close();
            window.BeginAnimation(UIElement.OpacityProperty, anim);
        }
        protected override void OnDetaching()
        {
            AssociatedObject.Closing -= AssociatedObject_Closing;
        }
    }

Then in your window add a reference :

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:wt="clr-namespace:Desktop.Themes.WindowTask;assembly=Desktop.Themes"

Insert the behavior:

<i:Interaction.Behaviors>
     <wt:WindowClosingBehavior />
</i:Interaction.Behaviors>

Set AutoReverse To "True"

<Window.Triggers>
        <EventTrigger RoutedEvent="Window.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" AutoReverse="True" From="0" To="1" Duration="0:0:0.5" FillBehavior="HoldEnd" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!