Utiliser des commandes dans un ViewModel

Effectué

Vous avez vu comment obtenir des données auprès de vos vues modèles pour les envoyer à votre interface utilisateur, et comment utiliser la liaison bidirectionnelle pour récupérer des données dans vos vues modèles.

L’utilisation de telles liaisons bidirectionnelles est la meilleure façon de réagir aux modifications apportées depuis l’interface utilisateur chaque fois que des données changent. De nombreuses choses que nous voudrions gérer comme des événements peuvent l’être à l’aide de liaisons bidirectionnelles et de MVVM. Switch.IsToggled et Slider.Value sont d’autres exemples, qui peuvent être reflétés dans notre vue modèle en tant que valeur booléenne ou entière, sans devoir utiliser des événements.

Mais des événements, comme l’activation d’un Button ou d’un MenuItem, ne sont pas directement liés à des données qui changent. Ces interactions nécessitent toujours une gestion de type événement. Comme ces composants d’interface utilisateur appellent généralement de la logique avec les données, nous voulons cette logique sur la vue modèle. Cependant, si possible, nous ne souhaitons pas les gérer en tant qu’événements Clicked et Selected dans le code-behind. Nous voulons autant que possible être dans la vue modèle, car de cette façon, elle peut être testée.

Utiliser le modèle de commande

La plupart des contrôles .NET MAUI qui ont ce type d’interaction prennent en charge la liaison à une propriété qui expose une interface ICommand. Cette propriété est probablement nommée Command. Le bouton est un exemple :

<Button Text="Give Bonus" Command="{Binding GiveBonus}" />

Le contrôle sait quand appeler la commande. Par exemple, un bouton appelle la commande quand l’utilisateur clique sur celui-ci. Dans cet exemple, la commande est liée à la propriété GiveBonus de la vue modèle. Le type de propriété doit implémenter l’interface ICommand. Le code se présenterait comme ceci :

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonus {get; private set;}
    ...
}

L’interface ICommand a une méthode Execute qui est appelée quand un clic est effectué sur le bouton. De cette façon, ICommand.Execute remplace directement le code de gestion de l’événement Button.Click.

L’interface ICommand complète a deux méthodes de plus : CanExecute et CanExecuteChanged, qui sont utilisées pour déterminer si un contrôle doit apparaître activé ou désactivé.

Un bouton, par exemple, peut apparaître grisé si CanExecute retourne la valeur false.

Voici à quoi ressemble l’interface ICommand en C# :

public interface ICommand
{
    bool CanExecute(object parameter);
    void Execute(object parameter);
    event EventHandler CanExecuteChanged;
}

Utiliser la classe Command

Ce modèle de commande vous permet de garantir une séparation nette entre le comportement de l’interface utilisateur et l’implémentation de l’interface utilisateur. Mais il peut compliquer votre code si vous avez besoin de créer une classe distincte pour implémenter chaque gestionnaire d’événements.

Au lieu de créer plusieurs classes personnalisées qui implémentent l’interface, il est courant d’utiliser Command ou Command<T>. Ces classes implémentent ICommand mais exposent son comportement en tant que propriétés que vous pouvez définir dans votre vue modèle. Ceci vous permet d’implémenter la propriété GiveBonus décrite plus haut entièrement dans notre classe ViewModel :

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonus {get; private set;}
    public EmployeeViewModel(Employee model)
    {
        GiveBonus = new Command(GiveBonusExecute, GiveBonusCanExecute)
    }

    void GiveBonusExecute()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
    }
}

Dans ce code, le comportement Execute est fourni par la méthode GiveBonusExecute. Et CanExecute est fourni par GiveBonusCanExecute. Les délégués de ces méthodes sont passés au constructeur Command. Dans cet exemple, il n’y a aucune implémentation pour CanExecuteChanged.

Commandes avec paramètres

L’interface ICommand accepte un paramètre object pour les méthodes CanExecute et Execute. .NET MAUI implémente cette interface sans vérification des types via la classe Command. Les délégués que vous attachez à la commande doivent effectuer leur propre vérification des types pour garantir que le paramètre correct est passé. .NET MAUI fournit également l’implémentation de Command<T> où vous définissez le type de paramètre attendu. Quand vous créez une commande qui accepte un seul type de paramètre, utilisez Command<T>.

Les contrôles .NET MAUI qui implémentent le modèle de commande fournissent la propriété CommandParameter. En définissant cette propriété, vous pouvez passer un paramètre à la commande quand elle est appelée avec Execute ou quand elle vérifie l’état de la méthode CanExecute.

Dans cet exemple, la valeur de chaîne 25 est envoyée à la commande :

<Button Text="Give Bonus" Command="{Binding GiveBonus}" CommandParameter="25" />

La commande doit interpréter et convertir ce paramètre de chaîne. Il existe de nombreuses façons de fournir un paramètre fortement typé.

  • Au lieu d’utiliser la syntaxe d’attribut pour définir CommandParameter, utilisez des éléments XAML.

    <Button Text="Give Bonus" Command="{Binding GiveBonus}">
        <Button.CommandParameter>
            <x:Int32>25</x:Int32>
        </Button.CommandParameter>
    </Button>
    
  • Liez le CommandParameter à une instance du type correct.

  • Si le CommandParameter est lié au type incorrect, appliquez un convertisseur pour convertir la valeur en un type correct.

Contrôle de vos connaissances

1.

Quel est l’objectif de l’interface ICommand ?

2.

Comment les classes Command ou Command<T> peuvent-elles simplifier l’utilisation de l’interface ICommand ?

3.

Quel est le rôle de la propriété CommandParameter dans les contrôles .NET MAUI ?