Le moyen le plus simple consiste à implémenter le gestionnaire d'événements ButtonClick
et à invoquer la méthode Window.Close()
, mais comment le faire via une liaison Command
?
Je pense que dans des scénarios du monde réel, un simple gestionnaire de clics est probablement meilleur que les systèmes basés sur des commandes trop compliqués, mais vous pouvez faire quelque chose comme ça:
en utilisant RelayCommand de cet article http://msdn.Microsoft.com/en-us/magazine/dd419663.aspx
public class MyCommands
{
public static readonly ICommand CloseCommand =
new RelayCommand( o => ((Window)o).Close() );
}
<Button Content="Close Window"
Command="{X:Static local:MyCommands.CloseCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"/>
Il suffit d'un peu de XAML ...
<Window x:Class="WCSamples.Window1"
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
Executed="CloseCommandHandler"/>
</Window.CommandBindings>
<StackPanel Name="MainStackPanel">
<Button Command="ApplicationCommands.Close"
Content="Close Window" />
</StackPanel>
</Window>
Et un peu de C # ...
private void CloseCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
this.Close();
}
(adapté de cet article MSDN )
En fait, c'est c'est possible sans code C #. La clé est d'utiliser les interactions:
<Button Content="Close">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Pour que cela fonctionne, définissez simplement le x:Name
de votre fenêtre à "fenêtre", et ajoutez ces deux espaces de noms:
xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.Microsoft.com/expression/2010/interactions"
Cela nécessite que vous ajoutiez le SDK Expression Blend DLL à votre projet, en particulier Microsoft.Expression.Interactions
.
Si vous n'avez pas Blend, le SDK peut être téléchargé ici .
La solution la plus simple que je connaisse est de définir la propriété IsCancel
sur true de la fermeture Button
:
<Button Content="Close" IsCancel="True" />
Aucune liaison nécessaire, WPF le fera automatiquement pour vous!
Référence: propriété MSDN Button.IsCancel .
Pour .NET 4.5 SystemCommands , la classe fera l'affaire (les utilisateurs de .NET 4.0 peuvent utiliser l'extension WPF Shell google - Microsoft.Windows.Shell ou Nicholas Solution).
<Window.CommandBindings>
<CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}"
CanExecute="CloseWindow_CanExec"
Executed="CloseWindow_Exec" />
</Window.CommandBindings>
<!-- Binding Close Command to the button control -->
<Button ToolTip="Close Window" Content="Close" Command="{x:Static SystemCommands.CloseWindowCommand}"/>
Dans le code derrière, vous pouvez implémenter les gestionnaires comme ceci:
private void CloseWindow_CanExec(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CloseWindow_Exec(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.CloseWindow(this);
}
Au début, j'avais aussi un peu de mal à comprendre comment cela fonctionnait, donc je voulais publier une meilleure explication de ce qui se passe réellement.
D'après mes recherches, la meilleure façon de gérer des choses comme celle-ci est d'utiliser les liaisons de commandes. Ce qui se passe est un "Message" est diffusé à tout dans le programme. Donc, ce que vous devez faire, c'est utiliser le CommandBinding
. Ce que cela signifie essentiellement, c'est "Quand vous entendez ce Message, faites ceci".
Ainsi, dans la question, l'utilisateur tente de fermer la fenêtre. La première chose que nous devons faire est de configurer nos fonctions qui seront appelées lorsque le SystemCommand.CloseWindowCommand
est diffusé. Vous pouvez éventuellement affecter une fonction qui détermine si la commande doit être exécutée. Un exemple serait de fermer un formulaire et de vérifier si l'utilisateur a enregistré.
void CloseApp( object target, ExecutedRoutedEventArgs e ) {
/*** Code to check for State before Closing ***/
this.Close();
}
void CloseAppCanExecute( object sender, CanExecuteRoutedEventArgs e ) {
/*** Logic to Determine if it is safe to Close the Window ***/
e.CanExecute = true;
}
Maintenant, nous devons configurer la "connexion" entre le SystemCommands.CloseWindowCommand
et les CloseApp
et CloseAppCanExecute
<Window.CommandBindings>
<CommandBinding Command="SystemCommands.CloseWindowCommand"
Executed="CloseApp"
CanExecute="CloseAppCanExecute"/>
</Window.CommandBindings>
Vous pouvez omettre CanExecute si vous savez que la commande doit toujours pouvoir être exécutée. Save peut être un bon exemple selon l'application. Voici un exemple:
<Window.CommandBindings>
<CommandBinding Command="SystemCommands.CloseWindowCommand"
Executed="CloseApp"/>
</Window.CommandBindings>
Enfin, vous devez dire à UIElement d'envoyer le CloseWindowCommand.
<Button Command="SystemCommands.CloseWindowCommand">
C'est en fait une chose très simple à faire, il suffit de configurer le lien entre la commande et la fonction réelle à exécuter, puis de dire au contrôle d'envoyer la commande au reste de votre programme en disant "Ok tout le monde exécute vos fonctions pour la commande CloseWindowCommand".
C'est en fait une très belle façon de gérer cela, car vous pouvez réutiliser la fonction exécutée partout sans avoir un wrapper comme vous le feriez avec, par exemple, WinForms (en utilisant un ClickEvent et en appelant une fonction dans la fonction d'événement) comme:
protected override void OnClick(EventArgs e){
/*** Function to Execute ***/
}
Dans WPF, vous attachez la fonction à une commande et dites à l'UIElement d'exécuter la fonction attachée à la commande à la place.
J'espère que cela clarifie les choses ...
Une option que j'ai trouvée efficace consiste à définir cette fonction comme un comportement.
Le comportement:
public class WindowCloseBehavior : Behavior<Window>
{
public bool Close
{
get { return (bool) GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}
public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("Close", typeof(bool), typeof(WindowCloseBehavior),
new PropertyMetadata(false, OnCloseTriggerChanged));
private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = d as WindowCloseBehavior;
if (behavior != null)
{
behavior.OnCloseTriggerChanged();
}
}
private void OnCloseTriggerChanged()
{
// when closetrigger is true, close the window
if (this.Close)
{
this.AssociatedObject.Close();
}
}
}
Dans la fenêtre XAML, vous y définissez une référence et liez la propriété Close du comportement à une propriété booléenne "Close" sur votre ViewModel:
xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
<i:Interaction.Behaviors>
<behavior:WindowCloseBehavior Close="{Binding Close}" />
</i:Interaction.Behaviors>
Ainsi, à partir de la vue, affectez un ICommand pour modifier la propriété Close sur le ViewModel qui est lié à la propriété Close du comportement. Lorsque l'événement PropertyChanged est déclenché, le comportement déclenche l'événement OnCloseTriggerChanged et ferme l'AssociatedObject ... qui est la fenêtre.