Share via


Befehle

Browse sample. Durchsuchen Sie den Beispielcode

In einer .NET Multi-Platform App UI (.NET MAUI)-App, die das Model-View-ViewModel (MVVM)-Muster verwendet, werden Datenbindungen zwischen Eigenschaften im Ansichtsmodell definiert. Dies ist in der Regel eine Klasse, die von INotifyPropertyChanged abgeleitet ist, und Eigenschaften in der Ansicht, die in der Regel die XAML-Datei ist. Manchmal hat eine App Anforderungen, die über diese Eigenschaftsbindungen hinausgehen, indem der Benutzer Befehle auslösen muss, die etwas im ViewModel beeinflussen. Diese Befehle werden in der Regel ausgeführt, indem Sie auf Schaltflächen klicken oder auf den Bildschirm tippen. Normalerweise werden sie in der CodeBehind-Datei in einem Handler für das Clicked-Ereignis von Button oder das Tapped-Ereignis von TapGestureRecognizer verarbeitet.

Über die Befehlsschnittstelle kann jedoch ein alternativer Ansatz für das Implementieren von Befehlen verwendet werden, der für die MVVM-Architektur besser geeignet ist. Das Viewmodel kann Befehle enthalten, d.h. Methoden, die als Reaktion auf eine bestimmte Aktivität in der Ansicht ausgeführt werden, wie etwa einen Button Klick. Datenbindungen werden zwischen diesen Befehlen und Button definiert.

Um eine Datenbindung zwischen einem Button und einem Viewmodel zu ermöglichen, definiert das Button zwei Eigenschaften:

Um die Befehlsschnittstelle zu verwenden, definieren Sie eine Datenbindung, die auf die Command Eigenschaft des Button abzielt, wobei die Quelle eine Eigenschaft im Viewmodel vom Typ ICommand ist. Das Viewmodel enthält Code, der mit dieser ICommand Eigenschaft verbunden ist und ausgeführt wird, wenn die Schaltfläche angeklickt wird. Sie können CommandParameter auf beliebige Daten festlegen, um zwischen mehreren Schaltflächen zu unterscheiden, wenn diese an die gleiche ICommand Eigenschaft im ViewModel gebunden sind.

Viele andere Ansichten definieren Command und CommandParameter Eigenschaften. Alle diese Befehle können innerhalb eines ViewModels mit einem Ansatz gehandhabt werden, der nicht vom Benutzeroberflächen-Objekt in der Ansicht abhängt.

ICommands

Die ICommand Schnittstelle ist im System.Windows.Input Namespace definiert und besteht aus zwei Methoden und einem Ereignis:

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

Um die Befehlsschnittstelle zu verwenden, sollte Ihr ViewModel Eigenschaften vom Typ ICommand enthalten:

public ICommand MyCommand { private set; get; }

Die ViewModel-Klasse muss zudem auf eine Klasse verweisen, die die ICommand Schnittstelle implementiert. In der Ansicht ist die Command Eigenschaft von Button an diese Eigenschaft gebunden:

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

Wenn der Benutzer das Button-Element anklickt, ruft Button die Execute-Methode im ICommand-Objekt auf, das an dessen Command-Eigenschaft gebunden ist.

Wenn die Bindung zuerst in der Command-Eigenschaft von Button definiert wird und die Datenbindung sich ändert, ruft Button die CanExecute-Methode im ICommand-Objekt auf. Wenn CanExecutefalse zurückgibt, deaktiviert Button sich selbst. Das bedeutet, dass der entsprechende Befehl aktuell nicht verfügbar oder ungültig ist.

Button fügt ebenfalls einen Handler an das CanExecuteChanged-Ereignis von ICommand an. Das Ereignis wird innerhalb des ViewModels ausgelöst. Wenn dieses Ereignis ausgelöst wird, ruft Button erneut CanExecute auf. Das Button-Element aktiviert sich selbst, wenn CanExecutetrue zurückgibt, und es deaktiviert sich selbst, wenn CanExecutefalse zurückgibt.

Warnung

Verwenden Sie die IsEnabled-Eigenschaft von Button nicht, wenn Sie die Befehlsschnittstelle verwenden.

Wenn Ihr ViewModel eine Eigenschaft vom Typ ICommand definiert, muss das Viewmodel auch eine Klasse enthalten oder referenzieren, welche die ICommand Schnittstelle implementiert. Diese Klasse muss die Execute- und CanExecute-Methoden enthalten oder auf diese verweisen und das CanExecuteChanged-Ereignis auslösen, wenn die CanExecute-Methode einen unterschiedlichen Wert zurückgibt. Sie können die Command oder Command<T> in .NET MAUI enthaltene Klasse verwenden, um die ICommand Schnittstelle zu implementieren. Durch diese Klassen können Sie den Text der Execute- und CanExecute-Methoden in den Konstruktoren der Klassen festlegen.

Tipp

Verwenden Sie Command<T>, wenn Sie die CommandParameter Eigenschaft, um zwischen mehreren Ansichten zu unterscheiden, die an die gleiche ICommand Eigenschaft gebunden sind.und die Command Klasse, wenn dies nicht erforderlich ist.

Grundlegende Befehle

Die folgenden Beispiele veranschaulichen grundlegende Befehle, die in einem Ansichtsmodell implementiert wurden.

Die PersonViewModel-Klasse definiert drei Eigenschaften namens Name, Age und Skills, über die eine Person definiert wird.

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));
    }
}

Die unten dargestellte PersonCollectionViewModel Klasse erstellt neue Objekte vom Typ PersonViewModel und ermöglicht es dem Benutzer, Daten einzugeben. Zu diesem Zweck definiert die Klasse die Eigenschaften IsEditing vom Typ bool und PersonEdit vom Typ PersonViewModel. Darüber hinaus definiert die Klasse drei Eigenschaften vom Typ ICommand und eine Eigenschaft namens Persons vom Typ 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));
    }
}

In diesem Beispiel führen Änderungen an den drei ICommand Eigenschaften und der Persons Eigenschaft nicht dazu PropertyChanged ,dass Ereignisse ausgelöst werden. Diese Eigenschaften werden allesamt festgelegt, wenn die Klasse erstmalig erstellt wird und ändern sich nicht mehr.

Das folgende Beispiel zeigt die XAML, die PersonCollectionViewModel verwendet:

<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">
    <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 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>

In diesem Beispiel wird die BindingContext Eigenschaft der Seite auf die PersonCollectionViewModel gesetzt. Das Grid enthält ein Button mit dem Text Neu, dessen Command Eigenschaft an die NewCommand Eigenschaft im Viewmodel gebunden ist, ein Eingabeformular mit Eigenschaften, die an die IsEditing Eigenschaft gebunden sind, sowie Eigenschaften von PersonViewModel und zwei weitere Schaltflächen, die an die SubmitCommand und CancelCommand Eigenschaften des Viewmodel gebunden sind. Die ListView zeigt die Sammlung der bereits eingegebenen Personen an:

Der folgende Screenshot zeigt die aktivierte Schaltfläche Senden nachdem ein Alter festgelegt worden ist:

Person Entry.

Wenn der Benutzer erstmalig die Schaltfläche Neu drückt, aktiviert dies das Eingabeformular, deaktiviert aber den Neu Button. Der Benutzer gibt dann einen Namen, ein Alter und seine Qualifikationen ein. Während der Bearbeitung kann der Benutzer jederzeit auf Cancel (Abbrechen) drücken, um von vorne zu beginnen. Die Schaltfläche Submit (Senden) wird nur aktiviert, wenn ein Name und ein gültiges Alter eingegeben wurden. Wenn auf die Schaltfläche Submit (Senden) gedrückt wird, wird die Person an die Collection übertragen, die von ListView angezeigt wird. Nachdem auf Cancel (Abbrechen) oder Submit (Senden) gedrückt wurde, wird das Eingabeformular zurückgesetzt, und die Schaltfläche New (Neu) wird wieder aktiviert.

Die Logik für die Schaltflächen New, Submit und Cancel wird in PersonCollectionViewModel über die Definitionen der NewCommand-, SubmitCommand- und CancelCommand-Eigenschaften verarbeitet. Der Konstruktor von PersonCollectionViewModel legt diese drei Eigenschaften auf Objekte vom Typ Command fest.

Ein Konstruktor der Klasse Command erlaubt Ihnen die Übergabe von Argumenten des Typs Action und Func<bool>, die den Methoden Execute und CanExecute entsprechen. Diese Aktion und Funktion können als Lambda-Funktionen im Command Konstruktor definiert werden:

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();
    }
    ···
}

Wenn der Benutzer auf die Schaltfläche New klickt, wird die execute-Funktion ausgeführt, die an den Command-Konstruktor übergeben wurde. Dadurch wird ein neues PersonViewModel-Objekt erstellt, ein Handler für das PropertyChanged-Ereignis des Objekts festgelegt, IsEditing wird auf true festgelegt, und die RefreshCanExecutes-Methode wird aufgerufen, die nach dem Konstruktor definiert wurde.

Die Command-Klasse implementiert nicht nur die ICommand-Schnittstelle, sondern definiert auch eine Methode namens ChangeCanExecute. Ein ViewModel sollte ChangeCanExecute für eine ICommand Eigenschaft immer dann aufrufen, wenn etwas passiert, das den Rückgabewert der CanExecute Methode ändern könnte. Durch einen Aufruf von ChangeCanExecute löst die Command-Klasse die CanExecuteChanged-Methode aus. An das Button-Element ist ein Handler für dieses Ereignis angefügt, und das Element reagiert, indem CanExecute erneut aufgerufen wird. Anschließend aktiviert es sich je nach Rückgabewert der Methode selbst.

Wenn die execute-Methode von NewCommandRefreshCanExecutes aufruft, erhält die NewCommand-Eigenschaft einen Aufruf von ChangeCanExecute, und Button ruft die canExecute-Methode auf, die nun false zurückgibt, da die IsEditing-Eigenschaft nun den Wert true aufweist.

Der PropertyChanged Handler für das neue PersonViewModel Objekt ruft die ChangeCanExecute Methode von SubmitCommand auf:

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;
            });
        ···
    }
    ···
}

Die canExecute-Funktion für SubmitCommand wird immer dann aufgerufen, wenn sich eine Eigenschaft in dem PersonViewModel-Objekt ändert, das bearbeitet wird. true wird nur zurückgegeben, wenn die Name-Eigenschaft mindestens ein Zeichen lang und Age größer als 0 ist. In diesem Fall wird die Schaltfläche Submit (Senden) aktiviert.

Die execute Funktion für Senden entfernt den Handler für geänderte Eigenschaften aus dem PersonViewModel, fügt das Objekt der Persons Sammlung hinzu und setzt alles in den Ausgangszustand zurück.

Die execute-Funktion für die Schaltfläche Cancel (Abbrechen) funktioniert wie die Schaltfläche Submit (Senden), das Objekt wird allerdings nicht zur Sammlung hinzugefügt:

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

Die canExecute-Methode gibt true immer dann zurück, wenn PersonViewModel bearbeitet wird.

Hinweis

Es ist nicht erforderlich, die execute- und canExecute-Methoden als Lambdafunktionen zu definieren. Sie können sie als private Methoden in das ViewModel schreiben und sie in den Command Konstruktoren referenzieren. Dieser Ansatz führt jedoch häufig dazu, dass viele Methoden in ViewModel vorhanden sind, auf die nur einmal verwiesen wird.

Verwendung von Befehlsparametern

Manchmal ist es praktisch, wenn eine oder mehrere Schaltflächen oder andere Objekte der Benutzeroberfläche die gleiche ICommand Eigenschaft im ViewModel gemeinsam nutzen. In diesem Fall verwenden Sie die CommandParameter Eigenschaft, um zwischen diesen Schaltflächen zu unterscheiden.

Sie können weiterhin die Command-Klasse für diese freigegebenen ICommand-Eigenschaften verwenden. Die Klasse definiert einen alternativen Konstruktor, der execute und canExecute Methoden mit Parametern vom Typ Object akzeptiert. Auf diese Weise wird CommandParameter an diese Methoden übergeben. Bei der Angabe eines CommandParameter ist es jedoch am einfachsten, die generische Klasse Command<T> zu verwenden, um den Objekttyp auf CommandParameter zu setzen. Die von Ihnen angegebenen execute- und canExecute-Methoden enthalten Parameter dieses Typs.

Im folgenden Beispiel wird eine Tastatur zum Eingeben von Dezimalzahlen veranschaulicht:

<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">
    <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>

In diesem Beispiel ist die Seite BindingContext ein DecimalKeypadViewModel. Die Entry Eigenschaft dieses ViewModels ist an die Text Eigenschaft eines Label gebunden. Alle Button Objekte sind an Befehle im ViewModel ClearCommand, BackspaceCommand und DigitCommand gebunden. Die 11 Schaltflächen für die 10 Ziffern und das Dezimaltrennzeichen sind an DigitCommand gebunden. CommandParameter unterscheidet zwischen diesen Schaltflächen. Der Wert, der auf CommandParameter gesetzt wird, ist im Allgemeinen derselbe wie der von der Schaltfläche angezeigte Text, mit Ausnahme des Dezimalpunkts, der aus Gründen der Übersichtlichkeit mit einem mittleren Punktzeichen dargestellt wird:

Decimal keyboard.

Die DecimalKeypadViewModel definiert eine Entry Eigenschaft vom Typ string und drei Eigenschaften vom Typ 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; }
}

Die Schaltfläche, die ClearCommand entspricht, ist immer aktiviert und setzt den Eintrag auf "0" zurück:

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

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

Da diese Schaltfläche immer aktiviert ist, ist es nicht notwendig, ein canExecute-Argument im Command-Konstruktor anzugeben.

Die RÜCKTASTE wird nur aktiviert, wenn die Länge des Eintrags größer als 1 ist oder wenn Entry nicht der Zeichenfolge „0“ entspricht:

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";
            });
        ···
    }
    ···
}

Die Logik für die execute-Funktion der RÜCKTASTE stellt sicher, dass Entry mindestens die Zeichenfolge „0“ enthält.

Die DigitCommand-Eigenschaft ist an 11 Schaltflächen gebunden, von denen jede über die CommandParameter-Eigenschaft identifiziert wird. Der DigitCommand ist auf eine Instanz der Command<T> Klasse gesetzt. Bei der Verwendung der Befehlsschnittstelle mit XAML sind die CommandParameter Eigenschaften in der Regel Zeichenfolgen, die den Typ des generischen Arguments darstellen. Die Funktionen execute und canExecute enthalten dann Argumente vom Typ 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("."));
            });
    }
    ···
}

Die execute-Methode fügt das Zeichenfolgenargument zur Entry-Eigenschaft hinzu. Wenn das Ergebnis jedoch mit einer Null beginnt (nicht jedoch mit einer Null und einem Dezimaltrennzeichen), muss diese erste Null mithilfe der Substring-Funktion entfernt werden. Die canExecute-Methode gibt false nur zurück, wenn das Argument das Dezimaltrennzeichen ist (d.h. es gibt an, dass das Dezimaltrennzeichen betätigt wird) und Entry bereits ein Dezimaltrennzeichen enthält. Alle execute-Methoden rufen die RefreshCanExecutes-Methode auf, die dann ChangeCanExecute für DigitCommand und ClearCommand aufruft. Dadurch wird sichergestellt, dass das Dezimaltrennzeichen und die RÜCKTASTE je nachdem, welche Ziffern eingegeben wurden, aktiviert oder deaktiviert werden.