Fading out a wpf window on close

36,710

Solution 1

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();
}

Solution 2

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.

Solution 3

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.

Share:
36,710

Related videos on Youtube

James
Author by

James

Java/C# Developer

Updated on July 09, 2022

Comments

  • James
    James almost 2 years

    I want to fade a window in/out in my application.
    Fading in occurs on Window.Loaded and I wanted to fade out on close (Window.Closed or Window.Closing). Fading in works perfectly, but Window.Closing is not allowed value for RoutedEvent property.
    What RoutedEvent should I be using for Close?

        <Window.Triggers>
            <EventTrigger RoutedEvent="Window.Loaded">
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:2" FillBehavior="HoldEnd" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger RoutedEvent="Window.Closing">
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:2" FillBehavior="HoldEnd" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Window.Triggers>
    

    I get a error on , Value 'Window.Closing' cannot be assigned to property 'RoutedEvent'. Invalid event name.

  • Aaron Powell
    Aaron Powell almost 15 years
    Where do you define the storyboard then?
  • Thomas Levesque
    Thomas Levesque almost 15 years
    You can define it in the resources (but then you can't access it directly in code-behind, you have to get it with FindResource
  • Nikos Tsokos
    Nikos Tsokos almost 15 years
    Dont forget to add <Storyboard Completed="closeStoryBoard_Completed"> to the closing storyboard.
  • Zakos
    Zakos over 10 years
    very nice , working fine with no code behind - this is the better answer imo
  • Pyritie
    Pyritie over 9 years
    If you get an error saying the storyboard needs a target, pass this to the closeStoryBoard.Begin();.
  • Yokomoko
    Yokomoko over 6 years
    This is not what the OP asked for and does not work. This reverses as soon as the animation of the loaded event finishes.
  • Tim Sylvester
    Tim Sylvester over 5 years
    Nicely done. Is it possible for the behavior to attach and detach without the window closing?
  • Patrick
    Patrick over 5 years
    @TimSylvester You mean with for instance a toggle? Sure, you can use another dependency property and bind it to your viewmodel for instance, and hook up the event when that property changes. I'm sure there are other ways too, depending on your need