[ACCEPTED]-Fading out a wpf window on close-routedevent

Accepted answer
Score: 32

Closing is not a routed event, so you can't 4 use it in an EventTrigger. Perhaps you could 3 start the storyboard in the handler of the 2 ClosingEvent in the code-behind and cancel 1 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();
}
Score: 9

I thought I'd add another solution of doing 18 this, using behaviors from the Expression 17 SDK and combining it with the solution from 16 @Thomas. Using that, we can define a "CloseBehavior" that 15 handles the code behind of starting a storyboard 14 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 13 behavior defines a storyboard as a dependency 12 property, so we can set it in xaml and when 11 the AssociatedObject (the window where we define the behavior) is 10 closing, this storyboard is started using 9 Storyboard.Begin(). Now, in xaml we simply add the behavior 8 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 7 the xml namespace i from the System.Windows.Interactivity 6 dll, and also that the window is referenced, so 5 it has to have a x:Name assigned. Now we simply 4 add the behavior to every window on which 3 we wish to execute a storyboard before closing 2 the application, instead of copying the 1 logic to every code-behind in each window.

Score: 1

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

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

Score: 0

This is even simpler and shorter. Add a 2 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 1 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>

More Related questions