Vue d'ensemble des commandes

L'exécution des commandes est un mécanisme d'entrée dans Windows Presentation Foundation (WPF) qui permet de gérer les entrées à un niveau plus sémantique que l'entrée de périphérique. Les opérations Copier, Couperet Coller sont des exemples de commandes présentes dans de nombreuses applications.

Cette vue d'ensemble définit les commandes présentes dans WPF, indique quelles classes font partie du modèle d'exécution de commande et comment utiliser et créer des commandes dans vos applications.

Cette rubrique contient les sections suivantes :

  • Qu'est-ce qu'une commande ?

  • Exemple de commande simple dans WPF

  • Quatre principaux concepts de l'exécution de commande dans WPF

  • Bibliothèque de commandes

  • Création de commandes personnalisées

Qu'est-ce qu'une commande ?

Les commandes ont plusieurs utilisations. La première est de séparer la sémantique et l'objet qui appelle une commande de la logique qui exécute la commande. Cela permet à des diverses sources d'appeler la même logique de commande et de personnaliser la logique de commande par rapport à différentes cibles. Par exemple, les opérations d'édition Copier, Couper et Coller, que l'on trouve dans de nombreuses applications, peuvent être appelées à l'aide d'actions utilisateur différentes, si elles sont implémentés à l'aide de commandes. Une application peut permettre à un utilisateur de couper des objets ou du texte en cliquant sur un bouton, en choisissant un élément d'un menu ou à l'aide d'une combinaison de touches, telle que CTRL+X. En utilisant des commandes, vous pouvez lier chaque type d'action utilisateur à la même logique.

Une autre utilisation des commandes est d'indiquer si une action est disponible. Pour revenir à l'exemple de couper un objet ou du texte, cette action ne présente d'intérêt que si un élément est sélectionné. Si un utilisateur essaie de couper un objet ou du texte sans avoir rien sélectionné, rien ne se passe. Pour l'indiquer à l'utilisateur, de nombreuses applications désactivent des boutons et des éléments de menu pour que l'utilisateur sache qu'il n'est pas possible d'exécuter cette action. Une commande peut indiquer si une action est possible en implémentant la méthode CanExecute. Un bouton peut s'inscrire à l'événement CanExecuteChanged et être désactivé si CanExecute retourne la valeur false ou être activé si CanExecute retourne la valeur true.

La sémantique d'une commande peut être cohérente dans les applications et les classes, mais la logique de l'action est propre à l'objet concerné par l'action. La combinaison de touches CTRL+X appelle la commande Couper dans les classes de texte, les classes d'image et les navigateurs Web, mais la logique réelle de l'opération Couper est définie par l'application qui exécute cette opération. Un RoutedCommand permet aux clients d'implémenter la logique. Un objet de texte peut couper le texte sélectionné dans le presse-papiers et un objet d'image peut couper l'image sélectionnée. Lorsqu'une application gère l'événement Executed, elle a accès à la cible de la commande et peut entreprendre l'action appropriée en fonction du type de la cible.

Exemple de commande simple dans WPF

La manière la plus simple d'utiliser une commande dans WPF consiste à utiliser une RoutedCommand prédéfinie de l'une des classes de la bibliothèque de commandes, utiliser un contrôle doté d'une prise en charge native pour gérer la commande et utiliser un contrôle doté d'une prise en charge native pour appeler une commande. La commande Paste est l'une des commandes prédéfinies de la classe ApplicationCommands. Le contrôle TextBox est doté d'une logique intégrée pour gérer la commande Paste. Et la classe MenuItem possède une prise en charge native pour appeler des commandes.

L'exemple suivant indique comment définir un MenuItem pour que, lorsque l'utilisateur clique dessus, il appelle la commande Paste dans une TextBox, si la TextBox a le focus clavier.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
            ' Creating the UI objects
            Dim mainStackPanel As New StackPanel()
            Dim pasteTextBox As New TextBox()
            Dim stackPanelMenu As New Menu()
            Dim pasteMenuItem As New MenuItem()

            ' Adding objects to the panel and the menu
            stackPanelMenu.Items.Add(pasteMenuItem)
            mainStackPanel.Children.Add(stackPanelMenu)
            mainStackPanel.Children.Add(pasteTextBox)

            ' Setting the command to the Paste command
            pasteMenuItem.Command = ApplicationCommands.Paste
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

Quatre principaux concepts de l'exécution de commande dans WPF

Le modèle de commande routée dans WPF peut être subdivisé en quatre concepts principaux : la commande, la source de la commande, la cible de la commande et la liaison de commande.

  • La commande est l'action à exécuter.

  • La source de la commande est l'objet qui appelle la commande.

  • La cible de la commande est l'objet sur lequel la commande est exécutée.

  • La liaison de commande (command binding) est l'objet qui mappe la logique de commande à la commande.

Dans l'exemple précédent, Paste est la commande, MenuItem est la source de la commande, TextBox est la cible de la commande, et la liaison de commande est fournie par le contrôle TextBox. Il convient de noter que la CommandBinding n'est pas toujours fournie par le contrôle qui correspond à la classe de cible de commande. Souvent, la CommandBinding doit être créée par le développeur d'applications ou la CommandBinding peut être jointe à un ancêtre de la cible de la commande.

Commandes

Les commandes de WPF sont créées en implémentant l'interface ICommand. ICommand expose deux méthodes :Execute et CanExecute, et l'événement CanExecuteChanged. Execute effectue les actions associées à la commande. CanExecute détermine si la commande peut s'exécuter sur la cible de la commande actuelle. CanExecuteChanged se déclenche lorsque le gestionnaire de commandes qui centralise les opérations d'exécution de commandes détecte une modification dans la source de la commande qui peut invalider une commande déclenchée mais pas encore exécutée par la liaison de commande. L'implémentation WPF de ICommand est la classe RoutedCommand et est le focus de cette vue d'ensemble.

Les principales sources d'entrée dans WPF sont la souris, le clavier, l'encre et les commandes routées. La plupart des entrées orientées périphérique utilisent un RoutedEvent pour signaler aux objets d'une page d'application qu'un événement d'entrée s'est produit. Une RoutedCommand fonctionne de la même manière. Les méthodes Execute et CanExecute d'une RoutedCommand ne contiennent pas la logique d'application pour la commande, mais elles déclenchent des événements routés qui se chargent du tunneling et de la propagation dans l'arborescence d'éléments jusqu'à ce qu'ils rencontrent un objet avec CommandBinding. CommandBinding contient les gestionnaires pour ces événements et ce sont ces gestionnaires qui effectuent la commande. Pour plus d'informations sur le routage d'événements dans WPF, consultez Vue d'ensemble des événements routés.

La méthode Execute sur une RoutedCommand déclenche les événements PreviewExecuted et Executed sur la cible de la commande. La méthode CanExecute sur une RoutedCommand déclenche les événements CanExecute et PreviewCanExecute sur la cible de la commande. Ces événements créent un tunnel et se propagent dans l'arborescence d'éléments jusqu'à ce qu'ils rencontrent un objet qui a une CommandBinding pour cette commande.

WPF fournit un jeu de commandes routées communes réparties sur plusieurs classes : MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands et EditingCommands. Ces classes contiennent uniquement des objets RoutedCommand et pas la logique d'implémentation de la commande. La logique d'implémentation est la responsabilité de l'objet sur lequel la commande est exécutée.

Sources de la commande

Une source de commande correspond à l'objet qui appelle la commande. MenuItem, Button et KeyGesture sont des exemples de sources de commande.

Les sources de commande dans WPF implémentent généralement l'interface ICommandSource.

ICommandSource expose trois propriétés : Command, CommandTarget et CommandParameter :

  • Command est la commande à exécuter lorsque la source de la commande est appelée.

  • CommandTarget est l'objet sur lequel la commande s'exécute. Il convient de noter que dans WPF, la propriété CommandTarget sur ICommandSource s'applique uniquement lorsque ICommand est une RoutedCommand. Si CommandTarget a la valeur ICommandSource et que la commande correspondante n'est pas une RoutedCommand, la cible de commande est ignorée. Si la CommandTarget n'est pas définie, l'élément avec le focus clavier devient la cible de la commande.

  • CommandParameter est un type de données défini par l'utilisateur utilisé pour passer des informations aux gestionnaires qui implémentent la commande.

Les classes WPF qui implémentent ICommandSource sont ButtonBase, MenuItem, Hyperlink et InputBinding. ButtonBase, MenuItem et Hyperlink appellent une commande lorsque l'utilisateur clique dessus, et InputBinding appelle une commande lorsque le InputGesture qui lui est associé est exécuté.

L'exemple suivant indique comment utiliser un MenuItem dans un ContextMenu comme source de la commande Properties.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
            Dim cmdSourcePanel As New StackPanel()
            Dim cmdSourceContextMenu As New ContextMenu()
            Dim cmdSourceMenuItem As New MenuItem()

            ' Add ContextMenu to the StackPanel.
            cmdSourcePanel.ContextMenu = cmdSourceContextMenu
            cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem)

            ' Associate Command with MenuItem.
            cmdSourceMenuItem.Command = ApplicationCommands.Properties
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;

En général, une source de commande écoutera l'événement CanExecuteChanged. Cet événement informe la source de la commande que la capacité de la commande à s'exécuter sur la cible de commande actuelle a peut-être changé. La source de la commande peut interroger l'état actuel de la RoutedCommand en utilisant la méthode CanExecute. La source de la commande peut ensuite se désactiver si la commande ne peut pas s'exécuter. Par exemple, un MenuItem devient grisé lorsqu'une commande ne peut pas s'exécuter.

Un InputGesture peut être utilisé comme source de commande. Deux types de mouvements d'entrée dans WPF sont : KeyGesture et MouseGesture. Un KeyGesture peut correspondre à un raccourci clavier (CTRL+C, par exemple). Un KeyGesture comprend une Key et un ensemble de ModifierKeys. Un MouseGesture comprend une MouseAction et un ensemble facultatif de ModifierKeys.

Pour qu'un InputGesture agisse comme source de commande, il doit être associé à une commande. Il existe plusieurs façons d'accomplir cette tâche. Vous pouvez utiliser une InputBinding.

L'exemple suivant indique comment créer une KeyBinding entre un KeyGesture et une RoutedCommand.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
            Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

            Dim OpenCmdKeybinding As New KeyBinding(ApplicationCommands.Open, OpenKeyGesture)

            Me.InputBindings.Add(OpenCmdKeybinding)
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);

Pour associer un InputGesture à une RoutedCommand, vous pouvez également ajouter le InputGesture à la InputGestureCollection de la RoutedCommand.

L'exemple suivant indique comment ajouter un KeyGesture à la InputGestureCollection d'une RoutedCommand.

            Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

            ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)
KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);

CommandBinding

Une CommandBinding associe une commande aux gestionnaires d'événements qui implémentent la commande.

La classe CommandBinding contient une propriété Command, et des événements PreviewExecuted, Executed, PreviewCanExecute et CanExecute.

Command est la commande à laquelle la CommandBinding est associée. Les gestionnaires d'événements attachés aux événements PreviewExecuted et Executed implémentent la logique de commande. Les gestionnaires d'événements attachés aux événements PreviewCanExecute et CanExecute déterminent si la commande peut s'exécuter sur la cible de commande actuelle.

L'exemple suivant indique comment créer une CommandBinding à la racine Window d'une application. CommandBinding associe la commande Open aux gestionnaires Executed et CanExecute.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
            ' Creating CommandBinding and attaching an Executed and CanExecute handler
            Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

            Me.CommandBindings.Add(OpenCmdBinding)
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);

Ensuite, le ExecutedRoutedEventHandler et un CanExecuteRoutedEventHandler sont créés. Le ExecutedRoutedEventHandler ouvre un MessageBox qui affiche une chaîne indiquant que la commande a été exécutée. Le CanExecuteRoutedEventHandler affecte à la propriété CanExecute la valeur true.

Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

Une CommandBinding est attachée à un objet spécifique, tel que la Window racine de l'application ou un contrôle. L'objet auquel le CommandBinding est attaché définit la portée de la liaison. Par exemple, une CommandBinding attachée à un ancêtre de la cible de la commande peut être atteinte par l'événement Executed, mais une CommandBinding attachée à un descendant de la cible de commande ne peut pas être atteinte. Il s'agit d'une conséquence directe de la manière dont un RoutedEvent crée un tunnel et se propage à partir de l'objet qui déclenche l'événement.

Dans certaines situations, la CommandBinding est attachée à la cible de la commande elle-même, comme avec la classe TextBox et les commandes Cut, Copy et Paste. Pourtant, il est souvent plus commode d'attacher la CommandBinding à un ancêtre de la cible de commande, comme la Window principale ou l'objet application, surtout si une même CommandBinding peut être utilisée pour plusieurs cibles de commande. Il s'agit là de décisions de conception que vous souhaiterez examiner lors de la création de votre infrastructure d'exécution de commande.

Cible de la commande

La cible de la commande est l'élément sur lequel la commande est exécutée. Dans le cas d'une RoutedCommand, la cible de la commande est l'élément à partir duquel le routage de Executed et CanExecute démarre. Comme indiqué précédemment, dans WPF, la propriété CommandTarget de ICommandSource s'applique uniquement lorsque ICommand est une RoutedCommand. Si CommandTarget a la valeur ICommandSource et que la commande correspondante n'est pas une RoutedCommand, la cible de commande est ignorée.

La source de la commande peut explicitement définir la cible de la commande. Si la cible de la commande n'est pas définie, l'élément avec le focus clavier sera utilisé comme cible de commande. L'utilisation de l'élément avec le focus clavier comme cible de commande présente notamment l'avantage de permettre au développeur d'applications d'utiliser la même source de commande pour appeler une commande sur plusieurs cibles sans devoir suivre la cible de la commande. Par exemple, si un MenuItem appelle la commande Coller dans une application dotée d'un contrôle TextBox et d'un contrôle PasswordBox, la cible peut être TextBox ou PasswordBox selon le contrôle qui a le focus clavier.

L'exemple suivant indique comment définir explicitement la cible de la commande dans la balise et le code sous-jacents.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
            ' Creating the UI objects
            Dim mainStackPanel As New StackPanel()
            Dim pasteTextBox As New TextBox()
            Dim stackPanelMenu As New Menu()
            Dim pasteMenuItem As New MenuItem()

            ' Adding objects to the panel and the menu
            stackPanelMenu.Items.Add(pasteMenuItem)
            mainStackPanel.Children.Add(stackPanelMenu)
            mainStackPanel.Children.Add(pasteTextBox)

            ' Setting the command to the Paste command
            pasteMenuItem.Command = ApplicationCommands.Paste
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

CommandManager

CommandManager prend en charge plusieurs fonctions liées à des commandes. Il fournit un jeu de méthodes statiques pour l'ajout et la suppression de gestionnaires d'événements PreviewExecuted, Executed, PreviewCanExecute et CanExecute dans un élément spécifique. Il permet également d'enregistrer des objets CommandBinding et InputBinding dans une classe spécifique. Le CommandManager permet en outre, grâce à l'événement RequerySuggested, de signaler à une commande qu'elle doit déclencher l'événement CanExecuteChanged.

La méthode InvalidateRequerySuggested force CommandManager à déclencher l'événement RequerySuggested. Cela peut s'avérer utile pour les conditions qui doivent désactiver/activer une commande mais qui ne sont pas reconnues par CommandManager.

Bibliothèque de commandes

WPF fournit un jeu de commandes prédéfinies. La bibliothèque de commandes se compose des classes suivantes : ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands et ComponentCommands. Ces classes fournissent des commandes telles que Cut, BrowseBack et BrowseForward, Play, Stop et Pause.

Nombre de ces commandes incluent un jeu de liaisons d'entrée par défaut. Par exemple, si vous spécifiez que votre application gère la commande de copie, vous obtenez automatiquement la configuration de clavier « CTRL+C ». Vous obtenez également des liaisons pour d'autres périphériques d'entrée, comme des mouvements de stylet Tablet PC et des informations vocales.

Lorsque vous référencez des commandes dans les différentes bibliothèques de commandes à l'aide de XAML, vous pouvez habituellement omettre le nom de la classe de bibliothèque qui expose la propriété de commande statique. En général, les noms de commande sont non équivoques comme chaînes, et les types propriétaires permettent de fournir un regroupement logique de commandes mais ne sont pas nécessaires pour éviter les ambiguïtés. Par exemple, vous pouvez spécifier Command="Cut" plutôt que Command="ApplicationCommands.Cut", plus prolixe. Ce mécanisme pratique est intégré au processeur WPF XAML pour les commandes (il s'agit plus précisément d'un comportement de convertisseur de type de ICommand, que le processeur WPF XAML référence au moment du chargement).

Création de commandes personnalisées

Si les commandes des classes de la bibliothèque de commandes ne répondent pas à vos besoins, vous pouvez créer vos propres commandes. Vous pouvez créer des commandes personnalisées de deux manières différentes. La première consiste à partir de zéro et à implémenter l'interface ICommand. L'autre manière (la plus fréquente) consiste à créer une RoutedCommand ou une RoutedUICommand.

Pour obtenir un exemple de la création d'un RoutedCommand personnalisé, consultez Créer un RoutedCommand personnalisé, exemple

Voir aussi

Tâches

Comment : implémenter ICommandSource

Comment : ajouter une commande à un MenuItem

Référence

RoutedCommand

CommandBinding

InputBinding

CommandManager

Concepts

Vue d'ensemble des entrées

Vue d'ensemble des événements routés

Autres ressources

Créer un RoutedCommand personnalisé, exemple (page éventuellement en anglais)