Udostępnij za pośrednictwem


Atrybut RelayCommand

Typ RelayCommand to atrybut, który umożliwia generowanie właściwości poleceń przekaźnika dla metod z adnotacjami. Jego celem jest całkowite wyeliminowanie kotła potrzebnego do zdefiniowania poleceń opakowujących prywatne metody w modelu widoków.

Uwaga

Aby można było pracować, metody z adnotacjami muszą znajdować się w klasie częściowej. Jeśli typ jest zagnieżdżony, wszystkie typy w drzewie składni deklaracji muszą być również oznaczone jako częściowe. Nie spowoduje to wystąpienia błędów kompilacji, ponieważ generator nie będzie mógł wygenerować innej częściowej deklaracji tego typu za pomocą żądanego polecenia.

Interfejsy API platformy:RelayCommand, ICommand, IRelayCommand, IAsyncRelayCommandIRelayCommand<T>, IAsyncRelayCommand<T>, , , TaskCancellationToken

Jak to działa

Atrybut RelayCommand może służyć do dodawania adnotacji do metody w typie częściowym, w następujący sposób:

[RelayCommand]
private void GreetUser()
{
    Console.WriteLine("Hello!");
}

Spowoduje to wygenerowanie polecenia w następujący sposób:

private RelayCommand? greetUserCommand;

public IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser);

Uwaga

Nazwa wygenerowanego polecenia zostanie utworzona na podstawie nazwy metody. Generator będzie używać nazwy metody i dołączać "Command" na końcu, a w razie obecności usunie prefiks "On". Ponadto w przypadku metod asynchronicznych sufiks "Async" jest również usuwany przed utworzeniem polecenia .

Parametry polecenia

Atrybut [RelayCommand] obsługuje tworzenie poleceń dla metod z parametrem . W takim przypadku automatycznie zmieni wygenerowane polecenie IRelayCommand<T> na zamiast niego, akceptując parametr tego samego typu:

[RelayCommand]
private void GreetUser(User user)
{
    Console.WriteLine($"Hello {user.Name}!");
}

Spowoduje to wygenerowanie następującego kodu:

private RelayCommand<User>? greetUserCommand;

public IRelayCommand<User> GreetUserCommand => greetUserCommand ??= new RelayCommand<User>(GreetUser);

Wynikowe polecenie automatycznie użyje typu argumentu jako argumentu typu.

Polecenia asynchroniczne

Polecenie [RelayCommand] obsługuje również zawijanie metod asynchronicznych za pośrednictwem IAsyncRelayCommand interfejsów i IAsyncRelayCommand<T> . Ta metoda jest obsługiwana automatycznie za każdym razem, gdy metoda zwraca Task typ. Przykład:

[RelayCommand]
private async Task GreetUserAsync()
{
    User user = await userService.GetCurrentUserAsync();

    Console.WriteLine($"Hello {user.Name}!");
}

Spowoduje to wykonanie następującego kodu:

private AsyncRelayCommand? greetUserCommand;

public IAsyncRelayCommand GreetUserCommand => greetUserCommand ??= new AsyncRelayCommand(GreetUserAsync);

Jeśli metoda przyjmuje parametr, wynikowe polecenie będzie również ogólne.

Istnieje szczególny przypadek, gdy metoda ma CancellationTokenwartość , ponieważ zostanie ona rozpropagowana do polecenia w celu włączenia anulowania. Oznacza to, że metoda podobna do następującej:

[RelayCommand]
private async Task GreetUserAsync(CancellationToken token)
{
    try
    {
        User user = await userService.GetCurrentUserAsync(token);

        Console.WriteLine($"Hello {user.Name}!");
    }
    catch (OperationCanceledException)
    {
    }
}

Spowoduje to przekazanie tokenu do opakowanej metody za pomocą wygenerowanego polecenia. Dzięki temu użytkownicy mogą po prostu wywołać IAsyncRelayCommand.Cancel ten token i zezwolić na poprawne zatrzymanie oczekujących operacji.

Włączanie i wyłączanie poleceń

Często przydaje się możliwość wyłączenia poleceń, a następnie późniejsze unieważnienie ich stanu i ponowne sprawdzenie, czy można je wykonać, czy nie. Aby to umożliwić, RelayCommand atrybut uwidacznia CanExecute właściwość , która może służyć do wskazania właściwości docelowej lub metody do oceny, czy można wykonać polecenie:

[RelayCommand(CanExecute = nameof(CanGreetUser))]
private void GreetUser(User? user)
{
    Console.WriteLine($"Hello {user!.Name}!");
}

private bool CanGreetUser(User? user)
{
    return user is not null;
}

W ten sposób jest wywoływany, CanGreetUser gdy przycisk jest najpierw powiązany z interfejsem użytkownika (np. do przycisku), a następnie jest wywoływany ponownie za każdym razem, gdy IRelayCommand.NotifyCanExecuteChanged jest wywoływany w poleceniu.

Na przykład w ten sposób polecenie może być powiązane z właściwością, aby kontrolować jego stan:

[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}"/>

W tym przykładzie wygenerowana SelectedUser właściwość będzie wywoływać GreetUserCommand.NotifyCanExecuteChanged() metodę za każdym razem, gdy zmienia się jego wartość. Interfejs użytkownika ma powiązanie sterujące z ButtonGreetUserCommand, co oznacza, że za każdym razem, gdy jest wywoływane jego CanExecuteChanged zdarzenie, wywoła metodę CanExecute ponownie. Spowoduje to obliczenie opakowanej CanGreetUser metody, która zwróci nowy stan przycisku na podstawie tego, czy wystąpienie wejściowe User (które w interfejsie użytkownika jest powiązane z właściwością SelectedUser ), null czy nie. Oznacza to, że zawsze, gdy SelectedUser zostanie zmieniona, zostanie włączona, a nie na podstawie tego, GreetUserCommand czy ta właściwość ma wartość, co jest pożądanym zachowaniem w tym scenariuszu.

Uwaga

Polecenie nie będzie automatycznie wiedzieć, kiedy wartość zwracana dla CanExecute metody lub właściwości uległa zmianie. Deweloper musi wywołać IRelayCommand.NotifyCanExecuteChanged polecenie , aby unieważnić polecenie i zażądać ponownej oceny połączonej CanExecute metody w celu zaktualizowania stanu wizualnego kontrolki powiązanej z poleceniem.

Obsługa współbieżnych wykonań

Za każdym razem, gdy polecenie jest asynchroniczne, można skonfigurować tak, aby zdecydować, czy zezwalać na współbieżne wykonywanie, czy nie. W przypadku używania atrybutu RelayCommand można to ustawić za pośrednictwem AllowConcurrentExecutions właściwości . Wartość domyślna to false, co oznacza, że do momentu oczekiwania na wykonanie polecenie zasygnalizuje jego stan jako wyłączony. Jeśli zamiast tego zostanie ustawiona wartość true, można kolejkować dowolną liczbę współbieżnych wywołań.

Należy pamiętać, że jeśli polecenie akceptuje token anulowania, token zostanie również anulowany, jeśli zażądano współbieżnego wykonania. Główną różnicą jest to, że jeśli współbieżne wykonania są dozwolone, polecenie pozostanie włączone i rozpocznie nowe żądane wykonanie bez oczekiwania na rzeczywiste ukończenie poprzedniego.

Obsługa wyjątków asynchronicznych

Istnieją dwa różne sposoby obsługi wyjątków za pomocą poleceń asynchronicznego przekaźnika:

  • Await and rethrow (default): gdy polecenie oczekuje na ukończenie wywołania, wszelkie wyjątki będą naturalnie zgłaszane w tym samym kontekście synchronizacji. Zwykle oznacza to, że zgłaszane wyjątki spowodują tylko awarię aplikacji, co jest zachowaniem zgodnym z działaniem poleceń synchronicznych (w przypadku gdy zgłaszane wyjątki również spowodują awarię aplikacji).
  • Wyjątki przepływu dla harmonogramu zadań: jeśli polecenie jest skonfigurowane do przepływu wyjątków do harmonogramu zadań, zgłaszane wyjątki nie spowodują awarii aplikacji, ale zamiast tego będą one dostępne za pośrednictwem uwidocznionego IAsyncRelayCommand.ExecutionTask , a także bubbling do TaskScheduler.UnobservedTaskException. Umożliwia to bardziej zaawansowane scenariusze (takie jak powiązanie składników interfejsu użytkownika z zadaniem i wyświetlanie różnych wyników na podstawie wyniku operacji), ale jest bardziej złożone, aby używać poprawnie.

Domyślne zachowanie polega na tym, że polecenia oczekują i wrócą wyjątki. Można to skonfigurować za pomocą FlowExceptionsToTaskScheduler właściwości :

[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task GreetUserAsync(CancellationToken token)
{
    User user = await userService.GetCurrentUserAsync(token);

    Console.WriteLine($"Hello {user.Name}!");
}

W takim przypadku nie jest to konieczne, try/catch ponieważ wyjątki nie spowodują już awarii aplikacji. Należy pamiętać, że spowoduje to również automatyczne ponowne dodawanie innych niepowiązanych wyjątków, dlatego należy dokładnie zdecydować, jak podejść do poszczególnych scenariuszy i odpowiednio skonfigurować resztę kodu.

Anuluj polecenia dla operacji asynchronicznych

Jedną z ostatnich opcji poleceń asynchronicznych jest możliwość zażądania wygenerowania polecenia anulowania. Jest to opakowujące asynchroniczne ICommand polecenie przekaźnika, które może służyć do żądania anulowania operacji. To polecenie automatycznie sygnalizuje stan, aby odzwierciedlić, czy można go użyć w danym momencie. Na przykład jeśli połączone polecenie nie jest wykonywane, będzie zgłaszać jego stan jako również nie jest wykonywalny. Można go użyć w następujący sposób:

[RelayCommand(IncludeCancelCommand = true)]
private async Task DoWorkAsync(CancellationToken token)
{
    // Do some long running work...
}

Spowoduje DoWorkCancelCommand to również wygenerowanie właściwości. Może to być następnie powiązane z innym składnikiem interfejsu użytkownika, aby umożliwić użytkownikom anulowanie oczekujących operacji asynchronicznych.

Dodawanie atrybutów niestandardowych

Podobnie jak w przypadku obserwowalnych właściwościRelayCommand generator zawiera również obsługę atrybutów niestandardowych dla wygenerowanych właściwości. Aby to wykorzystać, możesz po prostu użyć [property: ] elementu docelowego na listach atrybutów za pomocą metod z adnotacjami, a zestaw narzędzi MVVM przekaże te atrybuty do wygenerowanych właściwości polecenia.

Rozważmy na przykład metodę podobną do następującej:

[RelayCommand]
[property: JsonIgnore]
private void GreetUser(User user)
{
    Console.WriteLine($"Hello {user.Name}!");
}

Spowoduje to wygenerowanie GreetUserCommand właściwości z atrybutem [JsonIgnore] . Można użyć jak najwięcej list atrybutów przeznaczonych dla metody , a wszystkie z nich zostaną przekazane do wygenerowanych właściwości.

Przykłady

  • Zapoznaj się z przykładową aplikacją (dla wielu struktur interfejsu użytkownika), aby zobaczyć, jak działa zestaw narzędzi MVVM Toolkit.
  • Więcej przykładów można również znaleźć w testach jednostkowych.