Atribut RelayCommand
Typ RelayCommand
je atribut, který umožňuje generování vlastností příkazu relay pro anotované metody. Jejím účelem je zcela eliminovat často používané příkazy, které jsou potřeba k definování příkazů obtékání privátních metod v modelu viewmodel.
Poznámka:
Aby bylo možné pracovat, musí být anotované metody v částečné třídě. Pokud je typ vnořený, musí být všechny typy ve stromu syntaxe deklarace také opatřeny poznámkami jako částečné. Pokud to neuděláte, dojde k chybám kompilace, protože generátor nebude moci vygenerovat jinou částečnou deklaraci tohoto typu pomocí požadovaného příkazu.
Rozhraní API platformy:
RelayCommand
,ICommand
,IRelayCommand
,IRelayCommand<T>
,IAsyncRelayCommand
,IAsyncRelayCommand<T>
, , ,Task
CancellationToken
Jak to funguje
Atribut RelayCommand
lze použít k anotaci metody v částečném typu, například takto:
[RelayCommand]
private void GreetUser()
{
Console.WriteLine("Hello!");
}
A vygeneruje příkaz podobný tomuto:
private RelayCommand? greetUserCommand;
public IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser);
Poznámka:
Název vygenerovaného příkazu se vytvoří na základě názvu metody. Generátor použije název metody a na konci připojí "Command" a v případě přítomnosti odstraní předponu "On". Navíc u asynchronních metod se přípona "Async" odstraní také před tím, než se "Command" zřetědí.
Parametry příkazu
Atribut [RelayCommand]
podporuje vytváření příkazů pro metody s parametrem. V takovém případě automaticky změní vygenerovaný příkaz na místo IRelayCommand<T>
toho, aby přijímal parametr stejného typu:
[RelayCommand]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
Výsledkem bude následující vygenerovaný kód:
private RelayCommand<User>? greetUserCommand;
public IRelayCommand<User> GreetUserCommand => greetUserCommand ??= new RelayCommand<User>(GreetUser);
Výsledný příkaz automaticky použije typ argumentu jako argument typu.
Asynchronní příkazy
Příkaz [RelayCommand]
také podporuje zabalení asynchronních metod prostřednictvím IAsyncRelayCommand
rozhraní a IAsyncRelayCommand<T>
rozhraní. Tato metoda se zpracovává automaticky, kdykoli metoda vrátí Task
typ. Například:
[RelayCommand]
private async Task GreetUserAsync()
{
User user = await userService.GetCurrentUserAsync();
Console.WriteLine($"Hello {user.Name}!");
}
Výsledkem bude následující kód:
private AsyncRelayCommand? greetUserCommand;
public IAsyncRelayCommand GreetUserCommand => greetUserCommand ??= new AsyncRelayCommand(GreetUserAsync);
Pokud metoda vezme parametr, výsledný příkaz bude také obecný.
Existuje zvláštní případ, kdy metoda má CancellationToken
, protože se rozšíří do příkazu pro povolení zrušení. To znamená, že metoda podobná této:
[RelayCommand]
private async Task GreetUserAsync(CancellationToken token)
{
try
{
User user = await userService.GetCurrentUserAsync(token);
Console.WriteLine($"Hello {user.Name}!");
}
catch (OperationCanceledException)
{
}
}
Výsledkem bude vygenerovaný příkaz, který předá token zabalené metodě. To umožňuje uživatelům pouze volat IAsyncRelayCommand.Cancel
signál k tomuto tokenu a umožnit správné zastavení čekajících operací.
Povolení a zakázání příkazů
Často je užitečné příkazy zakázat a později zneplatnit jejich stav a znovu je zkontrolovat, jestli se dají spustit, nebo ne. Aby to bylo možné podporovat, RelayCommand
atribut zveřejňuje CanExecute
vlastnost, kterou lze použít k označení cílové vlastnosti nebo metody, které se mají použít k vyhodnocení, zda lze provést příkaz:
[RelayCommand(CanExecute = nameof(CanGreetUser))]
private void GreetUser(User? user)
{
Console.WriteLine($"Hello {user!.Name}!");
}
private bool CanGreetUser(User? user)
{
return user is not null;
}
Tímto způsobem se vyvolá, CanGreetUser
když je tlačítko poprvé svázané s uživatelským rozhraním (např. s tlačítkem) a pak se znovu vyvolá při každém IRelayCommand.NotifyCanExecuteChanged
vyvolání na příkaz.
Například takto může být příkaz svázán s vlastností, aby mohl řídit jeho stav:
[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}"/>
V tomto příkladu vygenerovaná SelectedUser
vlastnost vyvolá GreetUserCommand.NotifyCanExecuteChanged()
metodu pokaždé, když se změní její hodnota. Uživatelské rozhraní má Button
řídicí vazbu na GreetUserCommand
, což znamená, že pokaždé, když CanExecuteChanged
je vyvolána jeho událost, zavolá svou CanExecute
metodu znovu. To způsobí vyhodnocení zabalené CanGreetUser
metody, která vrátí nový stav tlačítka na základě toho, zda User
vstupní instance (která je v uživatelském rozhraní vázána SelectedUser
na vlastnost), nebo null
ne. To znamená, že při každé SelectedUser
změně se povolí nebo ne na základě toho, GreetUserCommand
jestli má tato vlastnost hodnotu, což je požadované chování v tomto scénáři.
Poznámka:
Příkaz nebude automaticky vědět o tom, kdy se změnila návratová hodnota pro metodu CanExecute
nebo vlastnost. Je na vývojáři, aby volal IRelayCommand.NotifyCanExecuteChanged
zneplatnění příkazu a požádal o vyhodnocení propojené CanExecute
metody, aby se pak aktualizoval stav vizuálu ovládacího prvku vázaného na příkaz.
Zpracování souběžných spuštění
Kdykoli je příkaz asynchronní, můžete ho nakonfigurovat tak, aby se rozhodl, jestli chcete povolit souběžná spuštění, nebo ne. Při použití atributu RelayCommand
to lze nastavit prostřednictvím AllowConcurrentExecutions
vlastnosti. Výchozí hodnota je false
, což znamená, že dokud není spuštěno, příkaz bude signalizovat svůj stav jako zakázáno. Pokud je místo toho nastavená hodnota true
, může být zařazen do fronty libovolný počet souběžných vyvolání.
Všimněte si, že pokud příkaz přijme token zrušení, token se zruší také v případě, že se požaduje souběžné spuštění. Hlavní rozdíl spočívá v tom, že pokud jsou povoleny souběžné spuštění, příkaz zůstane povolený a spustí nové požadované spuštění bez čekání na dokončení předchozího spuštění.
Zpracování asynchronních výjimek
Asynchronní příkazy přenosu zpracovávají výjimky dvěma způsoby:
- Await a rethrow (default): Když příkaz čeká na dokončení vyvolání, všechny výjimky se přirozeně vyvolá ve stejném kontextu synchronizace. To obvykle znamená, že vyvolání výjimek způsobí pouze chybové ukončení aplikace, což je chování konzistentní s synchronními příkazy (kde dojde také k chybovému ukončení aplikace).
- Výjimky toku do plánovače úloh: Pokud je příkaz nakonfigurovaný tak, aby tok výjimek plánovače úloh, vyvolá se výjimky, které se vyvolají, aplikaci nehroutí, ale místo toho budou dostupné prostřednictvím vystavených
IAsyncRelayCommand.ExecutionTask
i bublajících se až doTaskScheduler.UnobservedTaskException
. To umožňuje pokročilejší scénáře (například vytvoření vazby komponent uživatelského rozhraní k úloze a zobrazení různých výsledků na základě výsledku operace), ale správné použití je složitější.
Výchozí chování má příkazy await a znovu narůstají výjimky. Tuto vlastnost lze nakonfigurovat prostřednictvím FlowExceptionsToTaskScheduler
vlastnosti:
[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task GreetUserAsync(CancellationToken token)
{
User user = await userService.GetCurrentUserAsync(token);
Console.WriteLine($"Hello {user.Name}!");
}
V takovém případě není potřeba, try/catch
protože výjimky už nebudou chybově ukončeny. Mějte na paměti, že to také způsobí, že ostatní nesouvisející výjimky nebudou automaticky znovu zřetězovat, takže byste měli pečlivě rozhodnout, jak přistupovat ke každému jednotlivému scénáři a správně nakonfigurovat zbytek kódu.
Zrušení příkazů pro asynchronní operace
Jednou z posledních možností pro asynchronní příkazy je možnost požadovat, aby se vygeneroval příkaz zrušit. Jedná se o zabalení ICommand
asynchronního příkazu relé, který lze použít k vyžádání zrušení operace. Tento příkaz automaticky signalizují stav, který bude odrážet, jestli se dá v daném okamžiku použít. Například pokud se propojený příkaz nespouštějí, oznámí jeho stav jako také spustitelný soubor. Můžete ho použít takto:
[RelayCommand(IncludeCancelCommand = true)]
private async Task DoWorkAsync(CancellationToken token)
{
// Do some long running work...
}
To způsobí DoWorkCancelCommand
také vygenerování vlastnosti. To pak může být vázáno na některé jiné součásti uživatelského rozhraní, aby uživatelé mohli snadno zrušit čekající asynchronní operace.
Přidání vlastních atributů
Stejně jako u pozorovatelných vlastnostíRelayCommand
generátor také zahrnuje podporu vlastních atributů pro vygenerované vlastnosti. K tomu můžete jednoduše použít [property: ]
cíl v seznamech atributů nad anotovanými metodami a sada MVVM Toolkit tyto atributy předá do vygenerovaných vlastností příkazu.
Představte si například takovou metodu:
[RelayCommand]
[property: JsonIgnore]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
Tím se vygeneruje GreetUserCommand
vlastnost s atributem [JsonIgnore]
nad ním. Můžete použít libovolný počet seznamů atributů, které cílí na metodu, a všechny z nich budou předány do vygenerovaných vlastností.
Příklady
- Podívejte se na ukázkovou aplikaci (pro více architektur uživatelského rozhraní) a podívejte se na sadu nástrojů MVVM v akci.
- Další příklady najdete také v testech jednotek.