c# - tutorial - wpf vs windows forms
Desvaneciendo una ventana de wpf al cerrar (5)
Quiero atenuar una ventana de entrada / salida en mi aplicación.
Se produce un desvanecimiento en Window.Loaded
y quise desvanecerme al cerrar ( Window.Closed
o Window.Closing
). El desvanecimiento funciona perfectamente, pero Window.Closing
no tiene valor permitido para la propiedad RoutedEvent
.
¿Qué RoutedEvent
debería utilizar para Cerrar?
<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>
Aparece un error, Value ''Window.Closing'' no se puede asignar a la propiedad ''RoutedEvent''. Nombre de evento inválido
El cierre no es un evento enrutado, por lo que no puede usarlo en un EventTrigger. Tal vez podrías iniciar el guión gráfico en el controlador de ClosingEvent en el código subyacente y cancelar el evento ... algo así:
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();
}
Establezca AutoReverse como "Verdadero"
<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>
Esto es aún más simple y más corto. Agregue un comportamiento de la siguiente manera:
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;
}
}
Luego, en su ventana, agregue una referencia:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:wt="clr-namespace:Desktop.Themes.WindowTask;assembly=Desktop.Themes"
Inserta el comportamiento:
<i:Interaction.Behaviors>
<wt:WindowClosingBehavior />
</i:Interaction.Behaviors>
No soy un experto en WPF, pero creo que a menos que cancele el evento de Cierre inicial, la ventana se habrá ido antes de que la animación se inicie.
Al recibir el evento Window.Closing, debe cancelar el evento e iniciar la animación. Cuando termine la animación, puede cerrar la ventana.
Pensé en agregar otra solución para hacer esto, usar comportamientos de Expression SDK y combinarlos con la solución de @Thomas. Al usar eso, podemos definir un "Comportamiento de cierre" que maneje el código detrás de iniciar un guión gráfico y cerrar la ventana cuando haya terminado.
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);
}
}
}
El comportamiento define un guión gráfico como una propiedad de dependencia, por lo que podemos establecerlo en xaml y cuando se cierra AssociatedObject
(la ventana donde definimos el comportamiento), este guión gráfico se inicia utilizando Storyboard.Begin()
. Ahora, en xaml simplemente agregamos el comportamiento a la ventana usando el siguiente 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>
Tenga en cuenta el espacio de nombres xml i
de System.Windows.Interactivity dll, y también que se hace referencia a la ventana, por lo que debe tener una x:Name
asignado. Ahora simplemente agregamos el comportamiento a cada ventana en la que deseamos ejecutar un guión gráfico antes de cerrar la aplicación, en lugar de copiar la lógica a cada código subyacente en cada ventana.