WPF - Utilisation des RoutedCommand

by Fabien Lavocat 13. August 2010 08:14

WPFLorsque l’on souhaite utiliser des patterns de développement tel que MVVM (Model View – View Model) il y a une chose à savoir maîtriser (parmi beaucoup d’autres), c’est les RoutedCommand. C’est objets vont nous permettre d’exécuter des actions lorsque des événements seront déclenchés dans l’interface utilisateur. Par exemple, lorsque vous allez cliquez sur un bouton, nous allons déclencher une action. Vous allez me dire, mais ça on sait déjà le faire de plusieurs manière :

Exemple A :

Côté XAML : <Button Content="Click Me!" Click="Button_Click" />
Côté code behind :
private void Button_Click(object sender, RoutedEventArgs e)
{
   
MessageBox.Show("Hello World!");
}

Exemple B :

En s’abonnant à l’événement Click directement dans le code behind : bt.Click += new RoutedEventHandler(Button_Click);

Utilisation simple des RoutedCommand

L’utilisation des patterns de développement, en particulier MVVM, nous insite à placer de moins en moins de code de ce style là (abonnement à des événements) dans le code behind. Voyons donc comment utiliser les RoutedCommand pour palier à ça. Une RoutedCommand est une classe qui implémente l’interface ICommand. Celle-ci posséde deux méthodes qui vont nous interesser, à savoir CanExecute et Execute. La première retourne un booléen disant si oui ou non l’action peut être déclenchée et la seconde représente l’action à exécuter.

Dans notre code behind, nous allons décrire une action qui permettra d’afficher un MessageBox (peu importe le déclencheur pour le moment) :

/// <summary>
/// Création de l'objet RoutedCommand.
/// </summary>
public static RoutedCommand HelloWorldCommand = new RoutedCommand();

/// <summary>
/// Détermine si la commande peut être exécutée ou non.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void HelloWorldCommandCanExecute(Object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute =
true;
}

/// <summary>
/// Exécute l'action.
/// </summary>
/// <param name="target"></param>
/// <param name="e"></param>
public void HelloWorldCommandExecuted(Object target, ExecutedRoutedEventArgs e)
{
   
MessageBox.Show("Hello World!");
}

Afin de pouvoir utiliser cette commande lorsque nous cliquerons sur un bouton, nous allons devoir déclarer cette commande dans le code XAML de cette façon :

<Window.CommandBindings>
    <CommandBinding Command="local:MainWindow.HelloWorldCommand"
                  
CanExecute="HelloWorldCommandCanExecute"
                  
Executed="HelloWorldCommandExecuted"/>
</Window.CommandBindings>

Maintenant, pour que celle-ci soit exécutée lorsque nous cliquons sur le boutton, nous devons associer cette commande à la propriété Command du Button : <Button Content="Click Me!" Command="local:MainWindow.HelloWorldCommand" />

Si vous cliquez sur le bouton, vous aurez un MessageBox avec inscrit “Hello World!”. Voilà, nous avons fait une première RoutedCommand de façon très simple. Allons un peu plus loin dans les possibilités offertes par ces objets.

Utilisation des CommandParameter

Ajoutons un TextBox à notre fenêtre. Nous allons afficher le texte du TextBox dans un MessageBox déclenché par le click du boutton, mais uniquement lorsqu’il y aura un texte dans le TextBox. Pour cela, passons le contenu du TextBox (propriété Text) en paramètre de la commande :

<StackPanel>
    <TextBox x:Name="txtName" />
    <Button Content="Click Me!"
          
Command="local:MainWindow.HelloWorldCommand"
          
CommandParameter="{Binding Path=Text, ElementName=txtName}" />
</StackPanel>

Nous allons lier la propriété CommandParameter à la propriété Text du contrôle txtName. Voici comment récupérer cette valeur dans le code C# :

/// <summary>
/// Détermine si la commande peut être exécutée ou non.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void HelloWorldCommandCanExecute(Object sender, CanExecuteRoutedEventArgs e)
{
   
// Il faut vérifier que nous avons bien un objet en paramètre.
   
if (e.Parameter == null) return;

   
// On récupére la valeur.
   
String value = e.Parameter.ToString();

   
// On autorise l'exécution de la commande uniquement
   
// si l'on a du texte.
    e.CanExecute = value.Trim().Length > 0;
}

/// <summary>
/// Exécute l'action
/// </summary>
/// <param name="target"></param>
/// <param name="e"></param>
public void HelloWorldCommandExecuted(Object target, ExecutedRoutedEventArgs e)
{
   
if (e.Parameter == null) return;
   
String value = e.Parameter.ToString();

   
MessageBox.Show(String.Format("Hello {0}!", value));
}

Voilà le résultat :

Sans titre

Sans titre

L’activation / désactivation du boutton est effectuée automatiquement lorsque le contenu du TextBox change.

Utilisation des InputBindings

Enfin, voyons comment utiliser les racourcis clavier pour exécuter des commandes. Nous allons lier la touche Entrée du clavier à la commande HelloWorldCommand :

<Window.InputBindings>
    <KeyBinding Key="Enter"
              
Command="local:MainWindow.HelloWorldCommand"
              
CommandParameter="{Binding Path=Text, ElementName=txtName}" />
</Window.InputBindings>

Lorsque vous appuyez sur la touche Entrée, si vous avez entré un texte dans le TextBox, la commande HelloWorldCommand se déclenchera.

Comme vous pouvez le constater, il est vraiment simple de créer ses propres commandes et de les utiliser dans vos applications WPF. Si vous souhaitez télécharger l’exemple que j’ai utilisé, cliquez sur le lien suivant : DemoCommandWPF.zip (55 ko).

Tags:

WPF

Comments

8/14/2010 12:34:34 AM #

Jc

L'art et la manière de rajouter des lignes de codes pour pas grands choses en plus, c'est assez amusant de voir que d'un côté on essaye d'avoir de moins en moins de code (voir évolution du VB.NET) et d'un autre on rajoute de la complexité nouvelle et plus longue pour faire idem qu'avant.
Le grands coup de gueule du moment est le temps de chargement à froid des applications WPF, même sous Seven,... Inadmissible.

Jc France

8/14/2010 2:32:42 AM #

fabien lavocat

Effectivement, dans ce cas là on rajoute des lignes de code, mais la création de ces RoutedCommand permet la réutilisation de ces commandes sans avoir a réécrire de code C# ! de plus cela permet une meilleur factorisation du code et la séparation Designer / Développeur.
Au niveau du temps de chargement trop important, c'est quand il y a des données à charger ?

fabien lavocat France

8/14/2010 10:12:11 PM #

Sylvain

Il me semble que dans  HelloWorldCommandExecuted le paramètre ne peut jamais être null... Est ce vraiment utile de faire le test en début de procédure ? Dans quel contexte ce paramètre pourrait-il être null ?
Merci bien pour cet article qui aide a voir plus clair sur la manière de coder proprement en respectant le MVVM...

Sylvain France

8/16/2010 12:11:22 AM #

Jc

A essayer sans qu'il y ait le fichier .xml de 2 Ko à charger! l'application WPF que je parle est un essai tout simple, une forme, une liste qui affiche le contenu d'un fichier xml de 2ko, l'application fait 47Ko. Déjà testé avec le framework 3, puis le 3.5 devait rendre le WPF utilisable, finalement refait avec le framework 4.0 et toujours aussi lourd, je comprends pourquoi le WPF ne prends toujours pas au quotidien, dommage...

Jc France

8/16/2010 1:23:34 AM #

fabien lavocat

@Sylvain : Et si, e.Parameter peut être null si le designer ne précise pas (ou mal) la propriété CommandParameter
@Jc : Je n'ai pas rencontré de problème de lourdeur dont tu parles, cela doit dépendre de la machine qui le lance ainsi que de l'OS

fabien lavocat France

8/17/2010 10:20:25 AM #

Benoit

@Jc: WPF peut-être long à charger (et plus encore à afficher) des fichiers textes (et ses dérivés comme le xml) si on fait ça à l'arrache (sans binding, sans les bon Streams etc.) mais en cherchant un peu sur la MSDN il n'est pas très difficile de trouver comment ça doit être fait pour que ça soit performant Smile Mais si on utilise WPF comme de l'ASP ou du WinForm, ça marche assez mal, c'est vrai.

@Fabien: C'est sympa les commands pour virer le code behind de la vue, mais pour les évènements non gérer par les commandes, comme ceux impliqué dans le drag&drop, y a un moyen de les virer aussi du code behind ? (sans s'embarquer dans des framework externe 10x plus gros que l'appli elle-même...)

Benoit France

8/18/2010 1:46:28 AM #

fabien lavocat

@Benoit: oui c'est tout à fait possible, mais ça devient juste un peu plus complexe en apparence, mais Thomas Lebrun nous a fait une super classe pour éviter tout problème : blogs.developpeur.org/.../...ent-est-d-clench.aspx

fabien lavocat France

8/19/2010 5:36:47 AM #

Benoit

Effectivement, sympa les Attached Behaviors, j'ai pu virer les .cs de mes views Smile
Par contre j'ai pas utilisé la classe générique de Thomas, pas envie d'avoir du code que je comprend pas vraiment dans mon appli ^^
Mais vivement que ça soit inclu directement dans WPF ce genre de chose, tout comme les RelayCommand... Le MVVM n'est pas très pratique sinon.

Benoit France

8/19/2010 9:23:51 AM #

fabien lavocat

Laissons le travail aux designers !!! Smile

fabien lavocat France

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading



About

Fabien Lavocat

Lavocat Fabien
Ingénieur Multimédia - TMM Communication



"Blog-Microsoft.fr is an independent blog and is not affiliated with, nor has it been authorized, sponsored, or otherwise approved by Microsoft Corporation."

Contactez-moi Send mail

MVP
Microsoft Most Valuable Professional
Client Application Development