This article has been localized into French by the community.
Utiliser les commandes WPF
Dans l'article précédent, nous avons discuté de nombreuses théories sur les commandes et leur fonctionnement. Dans ce chapitre, nous verrons comment utiliser réellement les commandes, en les affectant aux éléments de l'interface utilisateur et en créant des liaisons de commande qui les lient ensembles.
Nous allons débuter avec un exemple très simple :
<Window x:Class="WpfTutorialSamples.Commands.UsingCommandsSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UsingCommandsSample" Height="100" Width="200">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="NewCommand_Executed" CanExecute="NewCommand_CanExecute" />
</Window.CommandBindings>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="ApplicationCommands.New">New</Button>
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace WpfTutorialSamples.Commands
{
public partial class UsingCommandsSample : Window
{
public UsingCommandsSample()
{
InitializeComponent();
}
private void NewCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("The New command was invoked");
}
}
}
Nous avons défini un CommandBinding dans la Window en l'ajoutant à sa collection CommandBindings. Nous avons précisé quelle commande nous souhaitions utiliser (la commande New dans ApplicationCommands) ainsi que ses deux eventHandlers. L'interface visuelle se compose d'un unique bouton auquel nous associons la commande grâce à la propriété Command.
Dans le Code-behind, nous associons les deux événements à leur handler. Le handler CanExecute, que WPF appellera quand l'application est libre pour regarder si la commande est disponible, est très simple dans cet exemple car nous souhaitons que cette commande soit toujours disponible. Cela est précisé en affectant la valeur True à la propriété CanExecute de l'argument de l'événement.
Le handler Executed se contente d'afficher un texte dans une MessageBox quand la commande est appelée. Si vous exécutez cet exemple et cliquez sur le bouton vous verrez ce message. A noter que cette commande possède un raccourci-clavier défini par défaut, dont vous héritez en bonus. Plutôt que de cliquer sur le bouton, vous pouvez taper Ctrl+N sur le clavier le résultat sera le même.
Utiliser la méthode CanExecute
Dans le premier exemple nous avons implémenté un événement CanExecute qui retourne simplement True, ainsi le bouton est toujours accessible. Cependant cela n'est bien sûr pas vrai pour tous les boutons. Dans de nombreux cas vous souhaiterez que le bouton soit activé ou désactivé en fonction d'un certain statut défini dans votre application.
L’exemple le plus commun de cela est la bascule de boutons pour utiliser le presse-papier de Windows où vous souhaiterez que les boutons Couper et Copier soient activés uniquement lorsqu'un texte est sélectionné et le bouton Coller lorsqu'un texte est présent dans le presse-papier. C'est exactement ce que nous allons effectuer dans cet exemple :
<Window x:Class="WpfTutorialSamples.Commands.CommandCanExecuteSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandCanExecuteSample" Height="200" Width="250">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Cut" CanExecute="CutCommand_CanExecute" Executed="CutCommand_Executed" />
<CommandBinding Command="ApplicationCommands.Paste" CanExecute="PasteCommand_CanExecute" Executed="PasteCommand_Executed" />
</Window.CommandBindings>
<DockPanel>
<WrapPanel DockPanel.Dock="Top" Margin="3">
<Button Command="ApplicationCommands.Cut" Width="60">_Cut</Button>
<Button Command="ApplicationCommands.Paste" Width="60" Margin="3,0">_Paste</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace WpfTutorialSamples.Commands
{
public partial class CommandCanExecuteSample : Window
{
public CommandCanExecuteSample()
{
InitializeComponent();
}
private void CutCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (txtEditor != null) && (txtEditor.SelectionLength > 0);
}
private void CutCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
txtEditor.Cut();
}
private void PasteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = Clipboard.ContainsText();
}
private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
txtEditor.Paste();
}
}
}
Nous avons donc cette interface très simple avec deux boutons et un contrôle TextBox. Le premier bouton coupera le texte du TextBox dans le presse-papiers, et le second bouton collera les données depuis le presse-papiers dans le TextBox.
Dans le code behind, nous avons deux évènements par bouton. Le premier qui exécute l'action; c'est celui qui finit par _Executed. Le second qui rend accessible l'action sous condition; c'est celui qui se termine par _CanExecute. Dans chacun des CanExecute, j'ai appliqué la logique qui permet de décider quelle action est possible sur chaque bouton et j'ai assigné la valeur à CanExecute sur le EventArgs.
Le truc sympas avec ces mécanismes, c'est qu'il n'est pas nécessaire d'appeler les méthodes tout le temps pour que le comportement des boutons soient mis à jour, WPF le fait tout seul permettant ainsi de rendre l'interface tout le temps réactive.
Comportement par défaut d'une commande et CommandTarget
Comme nous l'avons vu dans l'exemple précédent, utiliser les handler de commands permets de piloter pas mal de comportement avec peu de code et des comportements standards. C'est probablement pourquoi les équipes WPF les prennent en charge pour vous. En réalité, nous aurions pu nous affranchir du code behind de l'exemple précédent car la textBox WPF supporte nativement les commandes "copier", "couper", "coller", "annuler" et "rétablir"
WPF réalise les évènements Executed et CanExecute pour vous, quand un text input comme une textbox reçoit le focus. Il suffit de surcharger ces évènements. C'est précisément ce que nous avons fait dans l'exemple précédent, mais si vous voulez seulement un comportement basique, vous pouvez laissez WPF et le textbox faire le boulot pour vous. Un petit exemple pour montrer à quel point c'est simple :
<Window x:Class="WpfTutorialSamples.Commands.CommandsWithCommandTargetSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandsWithCommandTargetSample" Height="200" Width="250">
<DockPanel>
<WrapPanel DockPanel.Dock="Top" Margin="3">
<Button Command="ApplicationCommands.Cut" CommandTarget="{Binding ElementName=txtEditor}" Width="60">_Cut</Button>
<Button Command="ApplicationCommands.Paste" CommandTarget="{Binding ElementName=txtEditor}" Width="60" Margin="3,0">_Paste</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>
</Window>
Aucun code behind n'est nécessaire pour cet exemple. WPF s'occupe de tout pour nous, mais uniquement parce que nous utilisons des commandes standards sur des contrôles spécifiques. La textbox fait le boulot pour nous.
Notez comme j'utilise les propriétés du CommandTarget sur le textbox. C'est nécessaire dans ce cas très particulier, car le WrapPanel ne prend pas en charge le focus comme le ferait une toolbar ou un menu, mais ça a tout de même du sens d'indiquer la cible de la commande.
Résumé
Utiliser des commandes est assez standard comme façon de faire même si cela implique du code en plus. Le gros point positif se fait surtout sentir lorsque l'on souhaite utiliser la même action depuis plusieurs endroits, où lorsque vous utilisez des commandes standards qui sont complètement supportées par WPF comme dans l'exemple précédent.