Verwenden von Befehlen in einer ViewModel-Komponente
Sie haben gesehen, wie Sie Daten von Ihren ViewModel-Komponenten auf Ihre Benutzeroberfläche abrufen und wie Sie bidirektionale Bindung verwenden können, um Daten wieder in Ihre ViewMode-Komponente zu integrieren.
Die Verwendung bidirektionaler Bindungen ist die bevorzugte Methode, auf Änderungen der Benutzeroberfläche zu reagieren, bei denen Daten geändert werden. Viele Dinge, die als Ereignisse behandelt werden, können unter Verwendung bidirektionaler Bindungen und des MVVM-Musters (Model View ViewModel) verarbeitet werden. Andere Beispiele sind Dinge wie Switch.IsToggled
und Slider.Value
, die in unserem ViewModel als booleschen oder ganzzahligen Wert widergespiegelt werden können, ohne Ereignisse verwenden zu müssen.
Aber es gibt auch Vorgänge wie z.B. eine Button
- oder MenuItem
-Aktivierung, die nicht direkt an eine Datenänderung gebunden sind. Diese Interaktionen erfordern dennoch ereignisähnliche Verarbeitung. Da diese UI-Komponenten in der Regel eine Art Von Logik mit den Daten aufrufen, möchten wir diese Logik für das ViewModel. Aber wir möchten sie nicht als Clicked
und Selected
-Ereignisse im CodeBehind behandeln, wenn möglich. Wir möchten so viel wie möglich im ViewModel sein, auf diese Weise ist es testbar.
Verwenden des Command-Musters
Viele der .NET MAUI-Steuerelemente, die über diese Art von Interaktionsunterstützung verfügen, unterstützen die Bindung an eine Eigenschaft, die eine ICommand
Schnittstelle verfügbar macht. Diese Eigenschaft hat höchstwahrscheinlich den Namen Command
. Das Steuerelement Button
ist ein Beispiel:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" />
Das Steuerelement weiß, wann der Befehl aufgerufen werden soll. Beispielsweise ruft eine Schaltfläche den Befehl auf, wenn auf sie geklickt wird. Der Befehl in diesem Beispiel ist an die GiveBonus
Eigenschaft des ViewModels gebunden. Der Eigenschaftentyp muss die ICommand
Schnittstelle implementieren. Der Code sähe in etwa wie im folgenden Beispiel aus:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
...
}
Die ICommand
-Schnittstelle umfasst eine Execute
-Methode, die aufgerufen wird, wenn auf die Schaltfläche geklickt wird. Auf diese Weise ist ICommand.Execute
eine direkte Ersetzung für den Button.Click
-Code zur Ereignisbehandlung.
Die vollständige ICommand
-Schnittstelle weist zwei weitere Methoden auf: CanExecute
und CanExecuteChanged
, die für die Ermittlung verwendet werden, ob ein Steuerelement als aktiviert oder deaktiviert angezeigt werden soll.
Eine Schaltfläche kann beispielsweise abgeblendet dargestellt werden, wenn CanExecute
FALSE zurückgibt.
So sieht die ICommand
Schnittstelle in C# aus:
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Verwenden der Command-Klasse
Dieses Befehlsmuster ermöglicht eine saubere Trennung von UI-Verhalten und UI-Implementierung. Der Code kann jedoch komplexer werden, wenn Sie eine separate Klasse zum Implementieren jedes Ereignishandlers erstellen müssen.
Statt mehrere benutzerdefinierte Klassen zu erstellen, die die Schnittstelle implementieren, wird üblicherweise auf Command
oder Command<T>
zurückgegriffen. Diese Klassen implementieren ICommand
, stellen jedoch ihr Verhalten als Eigenschaften in Ihrem ViewModel bereit, das Sie festlegen können. Auf diese Weise können Sie die zuvor beschriebene GiveBonus
-Eigenschaft vollständig in die ViewModel-Klasse implementieren:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
public EmployeeViewModel(Employee model)
{
GiveBonusCommand = new Command(GiveBonusExecute, GiveBonusCanExecute)
}
void GiveBonusExecute()
{
//logic for giving bonus
}
bool GiveBonusCanExecute()
{
//logic for deciding if "give bonus" button should be enabled.
}
}
In diesem Code wird das Execute
-Verhalten von der Methode GiveBonusExecute
bereitgestellt. Und CanExecute
wird von GiveBonusCanExecute
bereitgestellt. Delegaten für diese Methoden werden an den Konstruktor Command
übergeben. Dieses Beispiel enthält keine Implementierung für CanExecuteChanged
.
Vereinfachen mit dem MVVM-Toolkit
Die MVVM Toolkit-Bibliothek enthält Implementierungen von ICommand
, die als RelayCommand
und AsyncRelayCommand
bezeichnet werden. Es stellt auch Quellgeneratoren bereit, um diesen Code noch weiter zu vereinfachen. Im folgenden Beispiel wird der GiveBonusCommand
generiert. Dabei wird die aufzurufende Methode zum Ausführen festgelegt wie auch die Methode, mit der festgestellt werden kann, ob die Ausführung möglich ist. Das [RelayCommand]
-Attribut wird für die GiveBonus
-Methode verwendet und generiert den GiveBonusCommand
. Außerdem wird die CanExecute
-Eigenschaft des Attributs auf den Namen der Methode eingestellt, die mit der CanExecute
-Methode für den ICommand
verbunden werden soll. Dadurch wird der entsprechende Code generiert.
public partial class EmployeeViewModel : ObservableObject
{
public EmployeeViewModel(Employee model)
{
}
[RelayCommand(CanExecute = nameof(GiveBonusCanExecute))]
void GiveBonus()
{
//logic for giving bonus
}
bool GiveBonusCanExecute()
{
//logic for deciding if "give bonus" button should be enabled.
return true;
}
}
Das MVVM-Toolkit eignet sich auch für async
-Methoden, die in der .NET-Programmierung üblich sind.
Befehle mit Parametern
Die ICommand
Schnittstelle akzeptiert einen object
Parameter für die CanExecute
und Execute
-Methoden. .NET MAUI implementiert diese Schnittstelle ohne Typüberprüfung durch die Command
Klasse. Die Delegate, die Sie an den Befehl anfügen, müssen ihre eigene Typüberprüfung durchführen, um sicherzustellen, dass der richtige Parameter übergeben wird. .NET MAUI stellt außerdem die Command<T>
Implementierung bereit, in der Sie den erwarteten Parametertyp festlegen. Wenn Sie einen Befehl erstellen, der einen einzelnen Parametertyp akzeptiert, verwenden Sie Command<T>
.
.NET MAUI-Steuerelemente, die das Befehlsmuster implementieren, stellen die CommandParameter
Eigenschaft bereit. Durch Festlegen dieser Eigenschaft können Sie einen Parameter an den Befehl übergeben, wenn Sie ihn mit Execute
aufrufen oder wenn der Befehl die CanExecute
-Methode auf den Status überprüft.
In diesem Beispiel wird der Zeichenfolgenwert 25 an den Befehl gesendet:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" CommandParameter="25" />
Der Befehl müsste diesen Zeichenfolgenparameter interpretieren und konvertieren. Es gibt viele Möglichkeiten, einen stark typierten Parameter bereitzustellen.
Verwenden Sie anstelle der Attributsyntax zum Definieren von
CommandParameter
XAML-Elemente.<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}"> <Button.CommandParameter> <x:Int32>25</x:Int32> </Button.CommandParameter> </Button>
Binden Sie die
CommandParameter
an eine Instanz des richtigen Typs.Wenn die
CommandParameter
an den falschen Typ gebunden wird, wenden Sie einen Konverter an, um den Wert in den richtigen Typ zu konvertieren.