Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Der RelayCommand Typ ist ein Attribut, das das Generieren von Relaybefehlseigenschaften für kommentierte Methoden zulässt. Ihr Zweck ist es, den Boilerplate-Code, der zum Definieren von Kommandos erforderlich ist, die private Methoden in einem Viewmodel kapseln, komplett zu eliminieren.
Note
Um zu arbeiten, müssen kommentierte Methoden in einer partiellen Klasse vorhanden sein. Wenn der Typ geschachtelt ist, müssen auch alle Typen in der Deklarationssyntaxstruktur als Teil kommentiert werden. Andernfalls führt dies zu Kompilierungsfehlern, da der Generator keine andere partielle Deklaration dieses Typs für den angeforderten Befehl generieren kann.
Plattform-APIs:
RelayCommand, ,ICommand,IRelayCommand,IRelayCommand<T>IAsyncRelayCommand,IAsyncRelayCommand<T>, ,TaskCancellationToken
So funktioniert es
Das RelayCommand Attribut kann verwendet werden, um eine Methode in einem Teiltyp zu kommentieren, z. B.:
[RelayCommand]
private void GreetUser()
{
Console.WriteLine("Hello!");
}
Außerdem wird ein Befehl wie folgt generiert:
private RelayCommand? greetUserCommand;
public IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser);
Note
Der Name des generierten Befehls wird basierend auf dem Methodennamen erstellt. Der Generator wird den Methodennamen verwenden, am Ende „Command“ anfügen und das Präfix „On“ entfernen, falls vorhanden. Darüber hinaus wird bei asynchronen Methoden auch das Suffix „Async“ entfernt, bevor „Command“ angehängt wird.
Befehlsparameter
Das [RelayCommand] Attribut unterstützt das Erstellen von Befehlen für Methoden mit einem Parameter. In diesem Fall ändert er automatisch den generierten Befehl so, dass er stattdessen ein IRelayCommand<T> Parameter desselben Typs akzeptiert:
[RelayCommand]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
Dies führt zu folgendem generierten Code:
private RelayCommand<User>? greetUserCommand;
public IRelayCommand<User> GreetUserCommand => greetUserCommand ??= new RelayCommand<User>(GreetUser);
Der resultierende Befehl verwendet automatisch den Typ des Arguments als Typargument.
Asynchrone Befehle
Der [RelayCommand] Befehl unterstützt auch die Kapselung asynchroner Methoden über die IAsyncRelayCommand- und IAsyncRelayCommand<T>-Schnittstellen. Dies wird automatisch behandelt, wenn eine Methode einen Task Typ zurückgibt. Beispiel:
[RelayCommand]
private async Task GreetUserAsync()
{
User user = await userService.GetCurrentUserAsync();
Console.WriteLine($"Hello {user.Name}!");
}
Dies führt zu folgendem Code:
private AsyncRelayCommand? greetUserCommand;
public IAsyncRelayCommand GreetUserCommand => greetUserCommand ??= new AsyncRelayCommand(GreetUserAsync);
Wenn die Methode einen Parameter verwendet, ist der resultierende Befehl ebenfalls generisch.
Es gibt einen Sonderfall, wenn die Methode über ein CancellationToken verfügt, da dieses an den Befehl übergeben wird, um einen Abbruch zu ermöglichen. Das heißt, eine Methode wie folgt:
[RelayCommand]
private async Task GreetUserAsync(CancellationToken token)
{
try
{
User user = await userService.GetCurrentUserAsync(token);
Console.WriteLine($"Hello {user.Name}!");
}
catch (OperationCanceledException)
{
}
}
Führt dazu, dass der generierte Befehl ein Token an die gekapselte Methode übergibt. Dadurch können Aufrufer einfach IAsyncRelayCommand.Cancel aufrufen, um dieses Token zu signalisieren und damit ausstehende Vorgänge ordnungsgemäß beendet werden können.
Aktivieren und Deaktivieren von Befehlen
Es ist häufig hilfreich, Befehle zu deaktivieren und dann später den Zustand ungültig zu machen und erneut zu überprüfen, ob sie ausgeführt werden können oder nicht. Um dies zu unterstützen, macht das RelayCommand Attribut die CanExecute Eigenschaft verfügbar, die verwendet werden kann, um eine Zieleigenschaft oder Methode anzugeben, mit der ausgewertet werden kann, ob ein Befehl ausgeführt werden kann:
[RelayCommand(CanExecute = nameof(CanGreetUser))]
private void GreetUser(User? user)
{
Console.WriteLine($"Hello {user!.Name}!");
}
private bool CanGreetUser(User? user)
{
return user is not null;
}
Auf diese Weise wird CanGreetUser aufgerufen, wenn die Schaltfläche erstmals an die Benutzeroberfläche gebunden wird (z. B. an eine Schaltfläche), und dann jedes Mal erneut, wenn IRelayCommand.NotifyCanExecuteChanged für den Befehl aufgerufen wird.
So kann beispielsweise ein Befehl an eine Eigenschaft gebunden werden, um den Zustand zu steuern:
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private User? selectedUser;
<!-- Note: this example uses traditional XAML binding syntax -->
<Button
Content="Greet user"
Command="{Binding GreetUserCommand}"
CommandParameter="{Binding SelectedUser}"/>
In diesem Beispiel wird die generierte SelectedUser Eigenschaft jedes Mal die Methode GreetUserCommand.NotifyCanExecuteChanged() aufrufen, wenn sich ihr Wert ändert. Die Benutzeroberfläche hat eine Button-Bindung an GreetUserCommand, das heißt, jedes Mal, wenn das Ereignis CanExecuteChanged ausgelöst wird, wird die Methode CanExecute erneut aufgerufen. Dies bewirkt, dass die gekapselte Methode CanGreetUser ausgewertet wird, die den neuen Zustand für die Schaltfläche basierend darauf zurückgibt, ob die Eingabeinstanz User (die in der Benutzeroberfläche an die Eigenschaft SelectedUser gebunden ist) null ist oder nicht. Dies bedeutet, dass GreetUserCommand jedes Mal, wenn SelectedUser geändert wird, aktiviert wird oder nicht, je nachdem, ob diese Eigenschaft einen Wert hat, was in diesem Szenario das gewünschte Verhalten ist.
Note
Der Befehl erkennt nicht automatisch, wann sich der Rückgabewert der CanExecute Methode oder Eigenschaft geändert hat. Der Entwickler muss den Befehl IRelayCommand.NotifyCanExecuteChanged ungültig machen und die verknüpfte CanExecute Methode erneut auswerten, um dann den visuellen Zustand des an den Befehl gebundenen Steuerelements zu aktualisieren.
Umgang mit parallelen Ausführungen
Wenn ein Befehl asynchron ist, kann er konfiguriert werden, um zu entscheiden, ob gleichzeitige Ausführungen zulässig sind oder nicht. Bei Verwendung des RelayCommand Attributs kann dies über die AllowConcurrentExecutions Eigenschaft festgelegt werden. Der Standardwert ist false, was bedeutet, dass der Befehl, bis eine Ausführung aussteht, den Status als deaktiviert signalisiert. Wenn dieser Wert stattdessen auf true gesetzt ist, kann eine beliebige Anzahl von gleichzeitigen Aufrufen in die Warteschlange gestellt werden.
Beachten Sie, dass bei einem Befehl, der ein Abbruch-Token akzeptiert, das Token ebenfalls abgebrochen wird, wenn eine parallele Ausführung angefordert wird. Der Hauptunterschied besteht darin, dass, wenn gleichzeitige Ausführungen zulässig sind, der Befehl aktiviert bleibt und eine neue angeforderte Ausführung gestartet wird, ohne auf den Abschluss des vorherigen Vorgangs zu warten.
Behandlung asynchroner Ausnahmen
Es gibt zwei verschiedene Möglichkeiten, wie asynchrone Relaybefehle Ausnahmen behandeln:
- Warten und wiederholen (Standard): Wenn der Befehl auf den Abschluss eines Aufrufs wartet, werden alle Ausnahmen natürlich im selben Synchronisierungskontext ausgelöst. Das bedeutet in der Regel, dass ausgelöste Ausnahmen nur die App abstürzen würden. Dies ist ein Verhalten, das mit den synchronen Befehlen übereinstimmt (wenn Ausnahmen ausgelöst werden, stürzt auch die App ab).
-
Ausnahmen an den Taskplaner weiterleiten: Wenn ein Befehl so konfiguriert ist, dass Ausnahmen an den Taskplaner weitergeleitet werden, führen ausgelöste Ausnahmen nicht zum Absturz der App, sondern werden sowohl über das verfügbar gemachte
IAsyncRelayCommand.ExecutionTaskverfügbar als auch anTaskScheduler.UnobservedTaskExceptionweitergegeben. Dies ermöglicht komplexere Szenarien (z. B. das Binden von UI-Komponenten an die Aufgabe und das Anzeigen unterschiedlicher Ergebnisse basierend auf dem Ergebnis des Vorgangs), ist jedoch komplexer, um ordnungsgemäß zu verwenden.
Das Standardverhalten ist, dass Kommandos auf den Abschluss warten und Ausnahmen weiterwerfen. Dies kann über die FlowExceptionsToTaskScheduler Eigenschaft konfiguriert werden:
[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task GreetUserAsync(CancellationToken token)
{
User user = await userService.GetCurrentUserAsync(token);
Console.WriteLine($"Hello {user.Name}!");
}
In diesem Fall ist das try/catch nicht nötig, da Ausnahmen die App nicht mehr zum Absturz bringen. Beachten Sie, dass dies auch dazu führt, dass andere, nicht damit zusammenhängende Ausnahmen nicht automatisch erneut ausgelöst werden. Daher sollten Sie sorgfältig entscheiden, wie Sie in jedem einzelnen Fall vorgehen, und den restlichen Code entsprechend konfigurieren.
Abbrechen von Befehlen für asynchrone Vorgänge
Eine letzte Option für asynchrone Befehle ist die Möglichkeit, die Generierung eines Abbruchbefehls anzufordern. Dies ist ein ICommand Wrapper für einen asynchronen Relay-Befehl, mit dem der Abbruch eines Vorgangs angefordert werden kann. Dieser Befehl signalisiert automatisch seinen Zustand, um anzugeben, ob er zu einem bestimmten Zeitpunkt verwendet werden kann. Wenn beispielsweise der verknüpfte Befehl nicht ausgeführt werden kann, meldet er seinen Status ebenfalls als nicht ausführbar. Dies kann wie folgt verwendet werden:
[RelayCommand(IncludeCancelCommand = true)]
private async Task DoWorkAsync(CancellationToken token)
{
// Do some long running work...
}
Dies führt dazu, dass auch eine DoWorkCancelCommand Eigenschaft generiert wird. Dies kann dann an eine andere UI-Komponente gebunden werden, damit Benutzer ausstehende asynchrone Vorgänge problemlos abbrechen können.
Benutzerdefinierte Attribute hinzufügen
Genau wie bei feststellbaren Eigenschaften bietet der RelayCommand Generator auch Unterstützung für benutzerdefinierte Attribute für die generierten Eigenschaften. Um dies zu nutzen, können Sie das [property: ] Ziel einfach in Attributlisten über kommentierte Methoden verwenden, und das MVVM-Toolkit leitet diese Attribute an die generierten Befehlseigenschaften weiter.
Ziehen Sie beispielsweise eine Methode wie folgt in Betracht:
[RelayCommand]
[property: JsonIgnore]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
Dies erzeugt eine GreetUserCommand-Eigenschaft, über der sich das Attribut [JsonIgnore] befindet. Sie können beliebig viele Attributlisten verwenden, die auf die Methode abzielen, und alle werden an die generierten Eigenschaften weitergeleitet.
Beispiele
- Sehen Sie sich die Beispiel-App (für mehrere UI-Frameworks) an, um das MVVM-Toolkit in Aktion zu sehen.
- Weitere Beispiele finden Sie auch in den Komponententests.