Vue d’ensemble des commandes

La commande est un mécanisme d’entrée dans Windows Presentation Foundation (WPF) qui fournit une gestion des entrées au niveau sémantique supérieur à l’entrée de l’appareil. Les opérations Copier, Couper et Coller sont des exemples de commandes figurant dans de nombreuses applications.

Cette vue d’ensemble définit les commandes qui se trouvent dans WPF, les classes qui font partie du modèle de commande et comment utiliser et créer des commandes dans vos applications.

Cette rubrique contient les sections suivantes :

Qu’est-ce que les commandes

Les commandes ont plusieurs fonctions. 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 sources diverses d’appeler la même logique de commande, et permet de personnaliser celle-ci pour différentes cibles. Par exemple, les opérations d’édition Copier, Couper et Coller, qui figurent dans de nombreuses applications, peuvent être appelées à l’aide de différentes actions utilisateur si elles sont implémentées à l’aide de commandes. Une application peut permettre à un utilisateur de couper du texte ou des objets sélectionnés en cliquant sur un bouton, en choisissant un élément dans un menu ou en utilisant une combinaison de touches telle que Ctrl+X. Grâce aux commandes, vous pouvez lier chaque type d’action de l’utilisateur à la même logique.

Une autre fonction des commandes consiste à indiquer si une action est disponible. Si vous coupez un objet ou du texte, l’action n’a de sens que si quelque chose est sélectionné. Si un utilisateur essaie de couper un objet ou du texte sans rien avoir sélectionné, rien ne se passe. Pour indiquer cela à l’utilisateur, de nombreuses applications désactivent les boutons et les éléments de menu afin que l’utilisateur sache s’il est possible ou non d’exécuter une action. Une commande peut indiquer si une action est possible en implémentant la méthode CanExecute. Un bouton peut s’abonner à l’événement CanExecuteChanged et être désactivé si CanExecute retourne false, ou être activé si CanExecute retourne true.

La sémantique d’une commande peut être cohérente parmi plusieurs applications et classes, mais la logique de l’action est propre à l’objet sur lequel elle est exécutée. 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 qui effectue réellement 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 texte peut couper le texte sélectionné dans le Presse-papiers, tandis qu’un objet image peut couper l’image sélectionnée. Quand 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 cible.

Exemple de commande simple dans WPF

La façon la plus simple d’utiliser une commande dans WPF consiste à utiliser un prédéfini RoutedCommand à partir de l’une des classes de bibliothèque de commandes ; utiliser un contrôle qui prend en charge nativement la gestion de la commande et utiliser un contrôle qui prend en charge nativement l’appel d’une commande. La commande Paste est l’une des commandes prédéfinies de la classe ApplicationCommands. Le contrôle TextBox a une logique intégrée pour la gestion de la commande Paste. Et la classe MenuItem a une prise en charge native de l’appel des commandes.

L’exemple suivant montre comment configurer MenuItem pour effectuer une action spécifique quand un utilisateur clique dessus, à savoir appeler la commande Paste sur TextBox, en supposant que TextBox ait le focus clavier.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// 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;
' 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

Quatre principaux concepts de l’exécution de commandes WPF

Le modèle de commande routé dans WPF peut être divisé en quatre concepts principaux : la commande, la source de commande, la cible de 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 est l’objet qui mappe la logique de commande à la commande.

Dans l’exemple précédent, la commande Paste est la commande, MenuItem est la source de commande, TextBox est la cible de commande, et la liaison de commande est fournie par le contrôle TextBox. Il est important de noter que CommandBinding n’est pas toujours fourni par le contrôle qui représente la classe de la cible de commande. Bien souvent, CommandBinding doit être créé par le développeur d’applications, ou CommandBinding peut être associé à un ancêtre de la cible de commande.

Commandes

Les commandes dans WPF sont créées en implémentant l’interface ICommand . ICommand expose deux méthodes, Execute et CanExecute, ainsi qu’un é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 commande actuelle. CanExecuteChanged se déclenche si le gestionnaire de commandes qui centralise les opérations d’exécution de commandes détecte un changement dans la source de commande susceptible d’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 RoutedCommand classe 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. Les entrées qui sont plus orientées appareil utilisent RoutedEvent pour notifier aux objets d’une page d’application qu’un événement d’entrée s’est produit. RoutedCommand n’est pas différent. Les méthodes Execute et CanExecute de RoutedCommand ne contiennent pas la logique d’application de la commande. En revanche, elles déclenchent des événements routés qui se propagent dans l’arborescence des éléments jusqu’à ce qu’ils rencontrent un objet ayant CommandBinding. CommandBinding contient les gestionnaires de ces événements. Ce sont les gestionnaires qui exécutent la commande. Pour plus d’informations sur le routage des événements dans WPF, consultez Vue d’ensemble des événements routés.

La méthode Execute sur RoutedCommand déclenche les événements PreviewExecuted et Executed sur la cible de commande. La méthode CanExecute sur RoutedCommand déclenche les événements CanExecute et PreviewCanExecute sur la cible de commande. Ces événements se propagent dans l’arborescence d’éléments jusqu’à ce qu’ils rencontrent un objet ayant CommandBinding pour cette commande particulière.

WPF fournit un ensemble de commandes routées courantes réparties entre plusieurs classes : MediaCommands, , ApplicationCommandsNavigationCommands, , ComponentCommandset EditingCommands. Ces classes contiennent uniquement les objets RoutedCommand, et non la logique d’implémentation de la commande. La logique d’implémentation incombe à l’objet sur lequel la commande est exécutée.

Sources de commande

Une source de commande est l’objet qui appelle la commande. Voici des exemples de sources de commande : MenuItem, Button et KeyGesture.

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 quand la source de commande est appelée.

  • CommandTarget est l’objet sur lequel exécuter la commande. Il vaut la peine de noter que dans WPF, la CommandTarget propriété est ICommandSource applicable uniquement lorsque l’objet ICommand est un RoutedCommand. Si CommandTarget est défini sur ICommandSource et si la commande correspondante n’est pas RoutedCommand, la cible de commande est ignorée. Si CommandTarget n’est pas défini, l’élément qui a le focus clavier est la cible de commande.

  • CommandParameter est un type de données défini par l’utilisateur, qui permet de passer des informations aux gestionnaires implémentant la commande.

Les classes WPF qui implémentent ICommandSource sont ButtonBase, MenuItem, Hyperlinket InputBinding. ButtonBase, MenuItem et Hyperlink appellent une commande quand l’utilisateur clique sur ces derniers, et InputBinding appelle une commande quand le InputGesture associé est exécuté.

L’exemple suivant montre comment utiliser MenuItem dans ContextMenu en tant que source de commande pour la commande Properties.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
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;
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

En règle générale, une source de commande écoute l’événement CanExecuteChanged. Cet événement signale à la source de commande que la capacité de la commande à s’exécuter sur la cible de commande actuelle a peut-être changé. La source de commande peut interroger l’état actuel de RoutedCommand à l’aide de la méthode CanExecute. La source de commande peut ensuite se désactiver si la commande ne peut pas s’exécuter. C’est le cas, par exemple, de MenuItem qui devient grisé quand une commande ne peut pas s’exécuter.

InputGesture peut être utilisé en tant que source de commande. Deux types de mouvements d’entrée dans WPF sont les KeyGesture suivants : MouseGesture. Vous pouvez considérer un KeyGesture comme un raccourci clavier, par exemple Ctrl+C. Un KeyGesture est composé de Key et d’un ensemble de ModifierKeys. Un MouseGesture est composé de MouseAction et d’un ensemble facultatif de ModifierKeys.

Pour permettre à InputGesture de servir de source de commande, il doit être associé à une commande. Il existe plusieurs façons d’accomplir cela. Une première approche consiste à utiliser InputBinding.

L’exemple suivant montre comment créer KeyBinding entre KeyGesture et RoutedCommand.

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

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

this.InputBindings.Add(OpenCmdKeybinding);
Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

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

Me.InputBindings.Add(OpenCmdKeybinding)

Une autre approche permettant d’associer InputGesture à RoutedCommand consiste à ajouter InputGesture à InputGestureCollection sur RoutedCommand.

L’exemple suivant montre comment ajouter KeyGesture à InputGestureCollection de RoutedCommand.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)

CommandBinding

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

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

Command est la commande à laquelle CommandBinding est associé. 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 montre comment créer CommandBinding dans le Window racine 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
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);
' Creating CommandBinding and attaching an Executed and CanExecute handler
Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

Me.CommandBindings.Add(OpenCmdBinding)

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

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 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 OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub

CommandBinding est attaché à un objet spécifique, par exemple le Window racine de l’application ou un contrôle. L’objet auquel CommandBinding est attaché définit la portée de la liaison. Par exemple, un CommandBinding attaché à un ancêtre de la cible de commande est accessible à l’événement Executed, mais un CommandBinding attaché à un descendant de la cible de commande est inaccessible. Il s’agit d’une conséquence directe de la façon dont RoutedEvent se propage à partir de l’objet qui déclenche l’événement.

Dans certaines situations, CommandBinding est attaché à la cible de commande elle-même, par exemple avec la classe TextBox et les commandes Cut, Copy et Paste. Toutefois, il est bien souvent plus pratique d’attacher le CommandBinding à un ancêtre de la cible de commande, par exemple le Window principal ou l’objet Application, en particulier si le même CommandBinding peut être utilisé pour plusieurs cibles de commande. Il y a des décisions de conception à prendre en compte quand vous créez votre infrastructure de commandes.

Cible de commande

La cible de commande est l’élément sur lequel la commande est exécutée. En ce qui concerne RoutedCommand, la cible de commande est l’élément à partir duquel le routage de Executed et CanExecute commence. Comme indiqué précédemment, dans WPF, la CommandTarget propriété est ICommandSource applicable uniquement lorsque l’objet ICommand est un RoutedCommand. Si CommandTarget est défini sur ICommandSource et si la commande correspondante n’est pas RoutedCommand, la cible de commande est ignorée.

La source de commande peut définir explicitement la cible de commande. Si la cible de commande n’est pas définie, l’élément ayant le focus clavier est utilisé comme cible de commande. L’un des avantages liés à l’utilisation de l’élément ayant le focus clavier comme cible de commande est que cela permet au développeur de l’application d’utiliser la même source de commande pour appeler une commande sur plusieurs cibles sans avoir à effectuer le suivi de la cible de commande. Par exemple, si MenuItem appelle la commande Coller dans une application qui a un contrôle TextBox et un contrôle PasswordBox, la cible peut être TextBox ou PasswordBox en fonction du contrôle qui a le focus clavier.

L’exemple suivant montre comment définir explicitement la cible de commande dans le balisage et dans le code-behind.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// 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;
' 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

CommandManager

CommandManager remplit un certain nombre de fonctions associées aux commandes. Il fournit un ensemble de méthodes statiques qui permettent d’ajouter et de supprimer les gestionnaires d’événements PreviewExecuted, Executed, PreviewCanExecute et CanExecute pour un élément spécifique. Il permet d’inscrire des objets CommandBinding et InputBinding dans une classe spécifique. CommandManager permet également, via l’événement RequerySuggested, de notifier à une commande le moment où elle doit déclencher l’événement CanExecuteChanged.

La méthode InvalidateRequerySuggested force CommandManager à déclencher l’événement RequerySuggested. Cela est utile pour les conditions qui doivent désactiver/activer une commande, mais dont CommandManager n’a pas connaissance.

Bibliothèque de commandes

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

Bon 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 liaison clavier « Ctrl+C » Vous obtenez également des liaisons pour d’autres périphériques d’entrée, tels que les mouvements de stylet tablet PC et les informations vocales.

Lorsque vous référencez des commandes dans les différentes bibliothèques de commandes à l’aide de XAML, vous pouvez généralement omettre le nom de classe de la classe de bibliothèque qui expose la propriété de commande statique. En général, les noms des commandes ne sont pas plus ambigus que des chaînes, et les types propriétaires existent pour 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 la version plus détaillée Command="ApplicationCommands.Cut". Il s’agit d’un mécanisme pratique intégré au processeur XAML WPF pour les commandes (plus précisément, il s’agit d’un comportement de convertisseur de type dont le processeur XAML WPF fait référence au moment du ICommandchargement).

Création de commandes personnalisées

Si les commandes dans les classes de la bibliothèque de commandes ne répondent pas à vos besoins, vous pouvez créer vos propres commandes. Il existe deux façons de créer une commande personnalisée. La première consiste à partir de zéro et à implémenter l’interface ICommand. La seconde, l’approche la plus courante, consiste à créer RoutedCommand ou RoutedUICommand.

Pour obtenir un exemple de création de RoutedCommand personnalisé, consultez Créer un exemple RoutedCommand personnalisé.

Voir aussi