Partager via


Commandes

Parcourir l'exemple. Parcourir l'exemple

Dans une application d’interface utilisateur d’application multiplateforme .NET (.NET MAUI) qui utilise le modèle Modèle-View-ViewModel (MVVM), les liaisons de données sont définies entre les propriétés dans le viewmodel, qui est généralement une classe qui dérive de INotifyPropertyChanged, et des propriétés dans la vue, qui est généralement le fichier XAML. Parfois, une application a besoin d’aller au-delà de ces liaisons de propriétés en obligeant l’utilisateur à lancer des commandes qui affectent quelque chose dans le viewmodel. Ces commandes sont généralement signalées par des clics de bouton ou des appuis de doigt et sont traitées par un gestionnaire dans le fichier code-behind pour l’événement Clicked de Button ou l’événement Tapped d’un TapGestureRecognizer.

L’interface de commande offre une autre approche de l’implémentation de commandes qui est beaucoup mieux adaptée à l’architecture MVVM. Le viewmodel peut contenir des commandes, qui sont des méthodes exécutées en réaction à une activité spécifique dans l’affichage, comme un Button clic. Les liaisons de données sont définies entre ces commandes et le Button.

Pour autoriser une liaison de données entre un Button et un viewmodel, le Button définit deux propriétés :

Pour utiliser l’interface de commande, vous définissez une liaison de données qui cible la propriété Command de la Button, dont la source est une propriété dans le vue-modèle de type ICommand. Le viewmodel contient du code associé à cette ICommand propriété exécutée lorsque le bouton est cliqué. Vous pouvez définir la CommandParameter propriété sur des données arbitraires pour faire la distinction entre plusieurs boutons s’ils sont tous liés à la même ICommand propriété dans le viewmodel.

De nombreuses autres vues définissent également les propriétés Command et CommandParameter. Toutes ces commandes peuvent être gérées dans un viewmodel à l’aide d’une approche qui ne dépend pas de l’objet d’interface utilisateur dans la vue.

ICommands

L’interface ICommand est définie dans l’espace de noms System.Windows.Input et se compose de deux méthodes et d’un événement :

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

Pour utiliser l’interface de commande, votre viewmodel doit contenir des propriétés de type ICommand:

public ICommand MyCommand { private set; get; }

Le viewmodel doit également référencer une classe qui implémente l’interface ICommand . Dans la vue, la Command propriété d’un Button est liée à cette propriété :

<Button Text="Execute command"
        Command="{Binding MyCommand}" />

Lorsque l’utilisateur appuie sur la Button, Button appelle la méthode Execute dans l’objet ICommand lié à sa propriété Command.

Lorsque la liaison est définie pour la première fois sur la Command propriété de l’objet Buttonet lorsque la liaison de données change d’une certaine façon, elle Button appelle la CanExecute méthode dans l’objet ICommand . Si CanExecute retourne false, alors Button se désactive. Cela indique que la commande particulière n’est pas disponible ou n’est pas valide.

Le Button attache également un gestionnaire sur l’événement CanExecuteChanged de ICommand. L’événement doit être déclenché manuellement à partir du viewmodel chaque fois que les conditions changent qui affectent le CanExecute résultat. Lorsque cet événement est déclenché, Button appelle de nouveau CanExecute. Le Button s’active si CanExecute retourne true et se désactive si CanExecute retourne false.

Important

Contrairement à certains cadres d’interface utilisateur (tels que WPF), .NET MAUI ne détecte pas automatiquement quand la valeur de retour de CanExecute peut changer. Vous devez déclencher manuellement l’événement CanExecuteChanged (ou appeler ChangeCanExecute() sur la classe Command) chaque fois qu’une condition change qui affecterait le résultat CanExecute. Cette opération est généralement effectuée lorsque les propriétés qui CanExecute dépendent d’elles sont modifiées.

Note

Vous pouvez également utiliser la propriété IsEnabled de Button au lieu de la méthode CanExecute, ou conjointement avec elle. Dans .NET MAUI 7 et les versions antérieures, il n'était pas possible d'utiliser la propriété IsEnabled tout en utilisant l'interface de commande Button, car la valeur de retour de la méthode CanExecute avait toujours remplacé la propriété IsEnabled. Ceci est résolu dans .NET MAUI 8 et versions ultérieures ; la propriété IsEnabled est désormais utilisable sur les Button commandes. Toutefois, sachez que la propriété IsEnabled et la méthode CanExecute doivent toutes deux retourner true afin que le Button soit activé, et le contrôle parent doit également être activé.

Lorsque votre viewmodel définit une propriété de type ICommand, le viewmodel doit également contenir ou référencer une classe qui implémente l’interface ICommand . Cette classe doit contenir ou référencer les Execute méthodes, CanExecute et déclencher manuellement l’événement CanExecuteChanged chaque fois que la CanExecute méthode peut retourner une valeur différente. Vous pouvez utiliser la classe Command ou Command<T> incluse dans .NET MAUI pour implémenter l’interface ICommand. Ces classes vous permettent de spécifier les corps des méthodes Execute et CanExecute dans les constructeurs de classe.

Conseil / Astuce

Utilisez Command<T> lorsque vous utilisez la propriété CommandParameter pour distinguer entre plusieurs vues liées à la même propriété ICommand et la classe Command quand cela n’est pas nécessaire.

Commandes de base

Les exemples suivants illustrent les commandes de base implémentées dans un viewmodel.

La PersonViewModel classe définit trois propriétés nommées Name, Ageet Skills qui définissent une personne :

public class PersonViewModel : INotifyPropertyChanged
{
    string name;
    double age;
    string skills;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        set { SetProperty(ref name, value); }
        get { return name; }
    }

    public double Age
    {
        set { SetProperty(ref age, value); }
        get { return age; }
    }

    public string Skills
    {
        set { SetProperty(ref skills, value); }
        get { return skills; }
    }

    public override string ToString()
    {
        return Name + ", age " + Age;
    }

    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

La PersonCollectionViewModel classe ci-dessous crée de nouveaux objets de type PersonViewModel et permet à l’utilisateur de remplir les données. À cet effet, la classe définit IsEditing, de type boolet PersonEdit, de type PersonViewModel, des propriétés. En outre, la classe définit trois propriétés de type ICommand et une propriété nommée Persons de type IList<PersonViewModel>:

public class PersonCollectionViewModel : INotifyPropertyChanged
{
    PersonViewModel personEdit;
    bool isEditing;

    public event PropertyChangedEventHandler PropertyChanged;
    ···

    public bool IsEditing
    {
        private set { SetProperty(ref isEditing, value); }
        get { return isEditing; }
    }

    public PersonViewModel PersonEdit
    {
        set { SetProperty(ref personEdit, value); }
        get { return personEdit; }
    }

    public ICommand NewCommand { private set; get; }
    public ICommand SubmitCommand { private set; get; }
    public ICommand CancelCommand { private set; get; }

    public IList<PersonViewModel> Persons { get; } = new ObservableCollection<PersonViewModel>();

    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Dans cet exemple, les modifications des ICommand trois propriétés et de la Persons propriété ne provoquent pas la génération des événements PropertyChanged. Ces propriétés sont toutes définies lorsque la classe est créée pour la première fois et ne changent pas.

L’exemple suivant montre le code XAML qui consomme le PersonCollectionViewModel :

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.PersonEntryPage"
             Title="Person Entry"
             x:DataType="local:PersonCollectionViewModel">             
    <ContentPage.BindingContext>
        <local:PersonCollectionViewModel />
    </ContentPage.BindingContext>
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <!-- New Button -->
        <Button Text="New"
                Grid.Row="0"
                Command="{Binding NewCommand}"
                HorizontalOptions="Start" />

        <!-- Entry Form -->
        <Grid Grid.Row="1"
              IsEnabled="{Binding IsEditing}">
            <Grid x:DataType="local:PersonViewModel"
                  BindingContext="{Binding PersonEdit}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Label Text="Name: " Grid.Row="0" Grid.Column="0" />
                <Entry Text="{Binding Name}"
                       Grid.Row="0" Grid.Column="1" />
                <Label Text="Age: " Grid.Row="1" Grid.Column="0" />
                <StackLayout Orientation="Horizontal"
                             Grid.Row="1" Grid.Column="1">
                    <Stepper Value="{Binding Age}"
                             Maximum="100" />
                    <Label Text="{Binding Age, StringFormat='{0} years old'}"
                           VerticalOptions="Center" />
                </StackLayout>
                <Label Text="Skills: " Grid.Row="2" Grid.Column="0" />
                <Entry Text="{Binding Skills}"
                       Grid.Row="2" Grid.Column="1" />
            </Grid>
        </Grid>

        <!-- Submit and Cancel Buttons -->
        <Grid Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <Button Text="Submit"
                    Grid.Column="0"
                    Command="{Binding SubmitCommand}"
                    VerticalOptions="Center" />
            <Button Text="Cancel"
                    Grid.Column="1"
                    Command="{Binding CancelCommand}"
                    VerticalOptions="Center" />
        </Grid>

        <!-- List of Persons -->
        <ListView Grid.Row="3"
                  ItemsSource="{Binding Persons}" />
    </Grid>
</ContentPage>

Dans cet exemple, la propriété de la page BindingContext est définie sur le PersonCollectionViewModel. Le Grid contient un Button avec le texte New dont la propriété Command est liée à la propriété NewCommand dans le viewmodel, un formulaire d'entrée avec ses propriétés liées à la propriété IsEditing, ainsi que des propriétés de PersonViewModel, et deux autres boutons liés aux propriétés SubmitCommand et CancelCommand du viewmodel. La ListView affiche la collection de personnes déjà entrées :

La capture d’écran suivante montre le bouton Envoyer activé une fois qu’un âge a été défini :

Entrée d'utilisateur.

Lorsque l’utilisateur appuie d’abord sur le bouton Nouveau , cela active le formulaire d’entrée, mais désactive le bouton Nouveau . L’utilisateur entre ensuite un nom, un âge et des compétences. À tout moment pendant la modification, l’utilisateur peut appuyer sur le bouton Annuler pour recommencer. Uniquement lorsqu’un nom et un âge valide ont été entrés est le bouton Envoyer activé. Appuyez sur ce bouton Envoyer pour transférer la personne vers la collection affichée par le ListView. Une fois le bouton Annuler ou Envoyer enfoncé, le formulaire d’entrée est effacé et le bouton Nouveau est à nouveau activé.

Toute la logique des boutons New, Submit et Cancel est gérée dans PersonCollectionViewModel par le biais des définitions des propriétés NewCommand, SubmitCommand et CancelCommand. Le constructeur de PersonCollectionViewModel définit ces trois propriétés comme des objets de type Command.

Un constructeur de la classe Command vous permet de passer des arguments de type Action et Func<bool> correspondant aux méthodes Execute et CanExecute. Cette action et cette fonction peuvent être définies en tant que fonctions lambda dans le Command constructeur :

public class PersonCollectionViewModel : INotifyPropertyChanged
{
    ···
    public PersonCollectionViewModel()
    {
        NewCommand = new Command(
            execute: () =>
            {
                PersonEdit = new PersonViewModel();
                PersonEdit.PropertyChanged += OnPersonEditPropertyChanged;
                IsEditing = true;
                RefreshCanExecutes();
            },
            canExecute: () =>
            {
                return !IsEditing;
            });
        ···
    }

    void OnPersonEditPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        (SubmitCommand as Command).ChangeCanExecute();
    }

    void RefreshCanExecutes()
    {
        (NewCommand as Command).ChangeCanExecute();
        (SubmitCommand as Command).ChangeCanExecute();
        (CancelCommand as Command).ChangeCanExecute();
    }
    ···
}

Lorsque l’utilisateur clique sur le bouton Nouveau , la execute fonction transmise au Command constructeur est exécutée. Cela crée un objet PersonViewModel, définit un gestionnaire pour l'événement PropertyChanged de cet objet, définit IsEditing sur true, et appelle la méthode RefreshCanExecutes définie après le constructeur.

Outre l’implémentation de l’interface ICommand , la Command classe définit également une méthode nommée ChangeCanExecute. Votre viewmodel doit appeler ChangeCanExecute à chaque fois que quelque chose se produit qui est susceptible de modifier la valeur de retour de la méthode CanExecute. Un appel à ChangeCanExecute entraîne la classe Command à déclencher l’événement CanExecuteChanged. Le Button a associé un gestionnaire à cet événement et réagit en appelant à nouveau CanExecute, puis en s'activant en fonction de la valeur de retour de cette méthode.

Lorsque la méthode execute de NewCommand appelle RefreshCanExecutes, la propriété NewCommand reçoit un appel à ChangeCanExecute, et l’élément Button appelle la méthode canExecute, qui retourne maintenant false parce que la propriété IsEditing est devenue true.

Le PropertyChanged gestionnaire du nouvel PersonViewModel objet appelle la ChangeCanExecute méthode de SubmitCommand:

public class PersonCollectionViewModel : INotifyPropertyChanged
{
    ···
    public PersonCollectionViewModel()
    {
        ···
        SubmitCommand = new Command(
            execute: () =>
            {
                Persons.Add(PersonEdit);
                PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
                PersonEdit = null;
                IsEditing = false;
                RefreshCanExecutes();
            },
            canExecute: () =>
            {
                return PersonEdit != null &&
                       PersonEdit.Name != null &&
                       PersonEdit.Name.Length > 1 &&
                       PersonEdit.Age > 0;
            });
        ···
    }
    ···
}

La fonction canExecute pour SubmitCommand est appelée chaque fois qu'une propriété est modifiée dans l'objet PersonViewModel en cours d'édition. Elle ne retourne true que lorsque la Name propriété a au moins un caractère long et Age est supérieure à 0. À ce stade, le bouton Envoyer devient activé.

La fonction executeSubmit supprime le gestionnaire de changement de propriété du PersonViewModel, ajoute l'objet à la collection Persons et retourne tout à son état initial.

La execute fonction du bouton Annuler effectue tout ce que fait le bouton Envoyer , à l’exception de l’ajout de l’objet à la collection :

public class PersonCollectionViewModel : INotifyPropertyChanged
{
    ···
    public PersonCollectionViewModel()
    {
        ···
        CancelCommand = new Command(
            execute: () =>
            {
                PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
                PersonEdit = null;
                IsEditing = false;
                RefreshCanExecutes();
            },
            canExecute: () =>
            {
                return IsEditing;
            });
    }
    ···
}

La méthode canExecute retourne true pendant l'édition d'un(e) PersonViewModel.

Note

Il n’est pas nécessaire de définir les méthodes execute et canExecute en tant que fonctions lambda. Vous pouvez les écrire en tant que méthodes privées dans le viewmodel et les référencer dans les Command constructeurs. Toutefois, cette approche peut entraîner de nombreuses méthodes référencées une seule fois dans le viewmodel.

Utilisation des paramètres de commande

Il est parfois pratique pour un ou plusieurs boutons, ou d’autres objets d’interface utilisateur, de partager la même ICommand propriété dans le viewmodel. Dans ce cas, vous pouvez utiliser la propriété CommandParameter pour faire la distinction entre les boutons.

Vous pouvez continuer à utiliser la Command classe pour ces propriétés partagées ICommand . La classe définit un constructeur alternatif qui accepte execute et canExecute méthodes avec des paramètres de type Object. Voici comment le CommandParameter est passé à ces méthodes. Toutefois, lors de la spécification d’un CommandParameter, il est plus simple d’utiliser la classe générique Command<T> pour spécifier le type de l’objet défini par CommandParameter. Les méthodes execute et canExecute que vous spécifiez ont des paramètres de ce type.

L’exemple suivant illustre un clavier permettant d’entrer des nombres décimaux :

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.DecimalKeypadPage"
             Title="Decimal Keyboard"
             x:DataType="local:DecimalKeypadViewModel">
    <ContentPage.BindingContext>
        <local:DecimalKeypadViewModel />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Button">
            <Setter Property="FontSize" Value="32" />
            <Setter Property="BorderWidth" Value="1" />
            <Setter Property="BorderColor" Value="Black" />
        </Style>
    </ContentPage.Resources>

    <Grid WidthRequest="240"
          HeightRequest="480"
          ColumnDefinitions="80, 80, 80"
          RowDefinitions="Auto, Auto, Auto, Auto, Auto, Auto"
          ColumnSpacing="2"
          RowSpacing="2"
          HorizontalOptions="Center"
          VerticalOptions="Center">
        <Label Text="{Binding Entry}"
               Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
               Margin="0,0,10,0"
               FontSize="32"
               LineBreakMode="HeadTruncation"
               VerticalTextAlignment="Center"
               HorizontalTextAlignment="End" />
        <Button Text="CLEAR"
                Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
                Command="{Binding ClearCommand}" />
        <Button Text="&#x21E6;"
                Grid.Row="1" Grid.Column="2"
                Command="{Binding BackspaceCommand}" />
        <Button Text="7"
                Grid.Row="2" Grid.Column="0"
                Command="{Binding DigitCommand}"
                CommandParameter="7" />
        <Button Text="8"
                Grid.Row="2" Grid.Column="1"
                Command="{Binding DigitCommand}"
                CommandParameter="8" />        
        <Button Text="9"
                Grid.Row="2" Grid.Column="2"
                Command="{Binding DigitCommand}"
                CommandParameter="9" />
        <Button Text="4"
                Grid.Row="3" Grid.Column="0"
                Command="{Binding DigitCommand}"
                CommandParameter="4" />
        <Button Text="5"
                Grid.Row="3" Grid.Column="1"
                Command="{Binding DigitCommand}"
                CommandParameter="5" />
        <Button Text="6"
                Grid.Row="3" Grid.Column="2"
                Command="{Binding DigitCommand}"
                CommandParameter="6" />
        <Button Text="1"
                Grid.Row="4" Grid.Column="0"
                Command="{Binding DigitCommand}"
                CommandParameter="1" />
        <Button Text="2"
                Grid.Row="4" Grid.Column="1"
                Command="{Binding DigitCommand}"
                CommandParameter="2" />
        <Button Text="3"
                Grid.Row="4" Grid.Column="2"
                Command="{Binding DigitCommand}"
                CommandParameter="3" />
        <Button Text="0"
                Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"
                Command="{Binding DigitCommand}"
                CommandParameter="0" />
        <Button Text="&#x00B7;"
                Grid.Row="5" Grid.Column="2"
                Command="{Binding DigitCommand}"
                CommandParameter="." />
    </Grid>
</ContentPage>

Dans cet exemple, la page BindingContext est un DecimalKeypadViewModel. La Entry propriété de ce viewmodel est liée à la Text propriété d’un Label. Tous les Button objets sont liés à des commandes dans le viewmodel : ClearCommand, BackspaceCommandet DigitCommand. Les 11 boutons pour les 10 chiffres et le point décimal partagent une liaison à DigitCommand. CommandParameter distingue entre ces boutons. La valeur définie CommandParameter est généralement la même que le texte affiché par le bouton, à l’exception du point décimal, qui à des fins de clarté est affiché avec un caractère de point central :

Clavier décimal.

Le DecimalKeypadViewModel définit une propriété Entry de type string et trois propriétés de type ICommand.

public class DecimalKeypadViewModel : INotifyPropertyChanged
{
    string entry = "0";

    public event PropertyChangedEventHandler PropertyChanged;
    ···

    public string Entry
    {
        private set
        {
            if (entry != value)
            {
                entry = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Entry"));
            }
        }
        get
        {
            return entry;
        }
    }

    public ICommand ClearCommand { private set; get; }
    public ICommand BackspaceCommand { private set; get; }
    public ICommand DigitCommand { private set; get; }
}

Le bouton correspondant à ClearCommand est toujours activé et réinitialise l'entrée à « 0 » :

public class DecimalKeypadViewModel : INotifyPropertyChanged
{
    ···
    public DecimalKeypadViewModel()
    {
        ClearCommand = new Command(
            execute: () =>
            {
                Entry = "0";
                RefreshCanExecutes();
            });
        ···
    }

    void RefreshCanExecutes()
    {
        ((Command)BackspaceCommand).ChangeCanExecute();
        ((Command)DigitCommand).ChangeCanExecute();
    }
    ···
}

Étant donné que le bouton est toujours activé, il n’est pas nécessaire de spécifier un canExecute argument dans le Command constructeur.

Le bouton Retour arrière est activé uniquement lorsque la longueur de l’entrée est supérieure à 1, ou si elle Entry n’est pas égale à la chaîne « 0 » :

public class DecimalKeypadViewModel : INotifyPropertyChanged
{
    ···
    public DecimalKeypadViewModel()
    {
        ···
        BackspaceCommand = new Command(
            execute: () =>
            {
                Entry = Entry.Substring(0, Entry.Length - 1);
                if (Entry == "")
                {
                    Entry = "0";
                }
                RefreshCanExecutes();
            },
            canExecute: () =>
            {
                return Entry.Length > 1 || Entry != "0";
            });
        ···
    }
    ···
}

La logique de la fonction pour le execute bouton Retour arrière garantit que le Entry est au moins une chaîne de « 0 ».

La DigitCommand propriété est liée à 11 boutons, chacun d’entre eux s’identifiant avec la CommandParameter propriété. La DigitCommand valeur est définie sur une instance de la Command<T> classe. Lorsque vous utilisez l’interface de commande avec XAML, les CommandParameter propriétés sont généralement des chaînes, qui est le type de l’argument générique. Les fonctions execute et canExecute ont ensuite des arguments de type string :

public class DecimalKeypadViewModel : INotifyPropertyChanged
{
    ···
    public DecimalKeypadViewModel()
    {
        ···
        DigitCommand = new Command<string>(
            execute: (string arg) =>
            {
                Entry += arg;
                if (Entry.StartsWith("0") && !Entry.StartsWith("0."))
                {
                    Entry = Entry.Substring(1);
                }
                RefreshCanExecutes();
            },
            canExecute: (string arg) =>
            {
                return !(arg == "." && Entry.Contains("."));
            });
    }
    ···
}

La execute méthode ajoute l’argument de chaîne à la Entry propriété. Toutefois, si le résultat commence par un zéro (mais pas un zéro et un point décimal), ce zéro initial doit être supprimé à l’aide de la Substring fonction. La canExecute méthode retourne false uniquement si l’argument est le point décimal (indiquant que le point décimal est enfoncé) et Entry contient déjà un point décimal. Toutes les execute méthodes appellent RefreshCanExecutes, qui appelle ensuite ChangeCanExecute pour les deux DigitCommand et ClearCommand. Cela garantit que le bouton décimal et le bouton retour arrière sont activés ou non en fonction de la séquence de chiffres actuellement entrés.