Atribut RelayCommand

Tipe RelayCommand adalah atribut yang memungkinkan pembuatan properti perintah relay untuk metode yang dianotasi. Tujuannya adalah untuk menghilangkan sepenuhnya kode boilerplate yang diperlukan untuk mendefinisikan perintah yang digunakan untuk membungkus metode privat dalam ViewModel.

Note

Untuk bekerja, metode anotasi harus berada di kelas parsial. Jika tipenya bersarang, semua tipe dalam pohon sintaks deklarasi juga harus diberi anotasi sebagai partial. Tidak melakukannya akan mengakibatkan kesalahan kompilasi, karena generator tidak akan dapat menghasilkan deklarasi parsial yang berbeda dari jenis tersebut dengan perintah yang diminta.

API Platform:RelayCommand, ICommand, IRelayCommand, IRelayCommand<T>, IAsyncRelayCommand, IAsyncRelayCommand<T>, Task, CancellationToken

Cara kerjanya

Atribut RelayCommand dapat digunakan untuk membuat anotasi metode dalam jenis parsial, seperti:

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

Dan itu akan menghasilkan perintah seperti ini:

private RelayCommand? greetUserCommand;

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

Note

Nama perintah yang dihasilkan akan dibuat berdasarkan nama metode. Generator akan menggunakan nama metode, menambahkan "Command" di akhir, dan menghapus awalan "On", jika ada. Selain itu, untuk metode asinkron, sufiks "Async" juga dihapus sebelum "Command" ditambahkan.

Parameter perintah

Atribut [RelayCommand] mendukung pembuatan perintah untuk metode dengan parameter . Dalam hal itu, perintah yang dihasilkan akan otomatis diubah menjadi IRelayCommand<T> sebagai gantinya, yang menerima parameter dengan tipe yang sama:

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

Ini akan menghasilkan kode yang dihasilkan berikut:

private RelayCommand<User>? greetUserCommand;

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

Perintah yang dihasilkan akan secara otomatis menggunakan jenis argumen sebagai argumen jenisnya.

Perintah asinkron

Perintah [RelayCommand] juga mendukung pembungkusan metode asinkron melalui antarmuka IAsyncRelayCommand dan IAsyncRelayCommand<T>. Hal ini ditangani secara otomatis setiap kali metode mengembalikan tipe Task. Contohnya:

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

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

Ini akan menghasilkan kode berikut:

private AsyncRelayCommand? greetUserCommand;

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

Jika metode mengambil parameter, perintah yang dihasilkan juga akan generik.

Ada kasus khusus ketika metode memiliki CancellationToken, karena hal itu akan diteruskan ke perintah agar memungkinkan pembatalan. Artinya, metode seperti ini:

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

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

Akan menyebabkan perintah yang dihasilkan meneruskan token ke metode yang dibungkus. Ini memungkinkan pengguna untuk cukup memanggil IAsyncRelayCommand.Cancel guna memberi sinyal pada token tersebut, sehingga operasi yang tertunda dapat dihentikan dengan benar.

Mengaktifkan dan menonaktifkan perintah

Seringkali berguna untuk dapat menonaktifkan perintah, dan untuk kemudian membatalkan statusnya dan meminta mereka memeriksa lagi apakah mereka dapat dieksekusi atau tidak. Untuk mendukung ini, RelayCommand atribut mengekspos CanExecute properti , yang dapat digunakan untuk menunjukkan properti target atau metode yang digunakan untuk mengevaluasi apakah perintah dapat dijalankan:

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

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

Dengan cara ini, CanGreetUser dipanggil ketika tombol pertama kali terikat ke UI (misalnya ke tombol), dan kemudian dipanggil lagi setiap kali IRelayCommand.NotifyCanExecuteChanged dipanggil pada perintah.

Misalnya, ini adalah bagaimana perintah dapat terikat ke properti untuk mengontrol statusnya:

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

Dalam contoh ini, properti yang dihasilkan SelectedUser akan memanggil GreetUserCommand.NotifyCanExecuteChanged() metode setiap kali nilainya berubah. UI memiliki pengikatan kontrol Button dengan GreetUserCommand, yang berarti setiap kali peristiwa CanExecuteChanged dipicu, metode CanExecute akan dipanggil lagi. Ini akan menyebabkan metode yang dibungkus CanGreetUser dievaluasi, yang akan mengembalikan status baru untuk tombol berdasarkan apakah instance input User (yang dalam UI terikat ke properti SelectedUser) bernilai null atau tidak. Ini berarti bahwa setiap kali SelectedUser diubah, GreetUserCommand akan diaktifkan atau tidak berdasarkan apakah properti tersebut memiliki nilai, yang merupakan perilaku yang diinginkan dalam skenario ini.

Note

Perintah tidak akan secara otomatis menyadari kapan nilai pengembalian untuk CanExecute metode atau properti telah berubah. Terserah pengembang untuk memanggil IRelayCommand.NotifyCanExecuteChanged untuk membatalkan perintah dan meminta metode tertaut CanExecute untuk dievaluasi lagi untuk kemudian memperbarui status visual kontrol yang terikat ke perintah.

Menangani eksekusi bersamaan

Setiap kali perintah asinkron, perintah dapat dikonfigurasi untuk memutuskan apakah akan mengizinkan eksekusi bersamaan atau tidak. Saat menggunakan RelayCommand atribut , ini dapat diatur melalui AllowConcurrentExecutions properti . Defaultnya adalah false, yang berarti bahwa sampai eksekusi tertunda, perintah akan memberi sinyal statusnya sebagai dinonaktifkan. Jika sebaliknya diatur ke true, sejumlah pemanggilan bersamaan dapat diantrekan.

Perhatikan bahwa jika perintah menerima token pembatalan, token juga akan dibatalkan jika eksekusi bersamaan diminta. Perbedaan utamanya adalah bahwa jika eksekusi bersamaan diizinkan, perintah akan tetap diaktifkan dan akan memulai eksekusi baru yang diminta tanpa menunggu yang sebelumnya benar-benar selesai.

Menangani pengecualian asinkron

Ada dua cara berbeda perintah relai asinkron menangani pengecualian:

  • Menunggu dan melempar ulang (bawaan): ketika perintah menunggu hingga pemanggilan selesai, pengecualian apa pun secara alami akan dilempar ulang dalam konteks sinkronisasi yang sama. Itu biasanya berarti bahwa pengecualian yang dilemparkan hanya akan merusak aplikasi, yang merupakan perilaku yang konsisten dengan perintah sinkron (di mana pengecualian yang dilemparkan juga akan merusak aplikasi).
  • Meneruskan pengecualian ke penjadwal tugas: jika perintah dikonfigurasi untuk meneruskan pengecualian ke penjadwal tugas, pengecualian yang terjadi tidak akan menyebabkan aplikasi mogok, tetapi akan tersedia melalui IAsyncRelayCommand.ExecutionTask yang diekspos serta diteruskan ke TaskScheduler.UnobservedTaskException. Ini memungkinkan skenario yang lebih canggih (seperti memiliki komponen UI yang mengikat tugas dan menampilkan hasil yang berbeda berdasarkan hasil operasi), tetapi lebih kompleks untuk digunakan dengan benar.

Perilaku bawaan adalah perintah menunggu dan melempar kembali pengecualian. Ini dapat dikonfigurasi melalui FlowExceptionsToTaskScheduler properti:

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

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

Dalam hal ini, try/catch tidak diperlukan, karena pengecualian tidak akan merusak aplikasi lagi. Perhatikan bahwa hal ini juga akan menyebabkan pengecualian lain yang tidak terkait tidak akan dilempar ulang secara otomatis, jadi Anda harus mempertimbangkan dengan saksama cara menangani setiap skenario dan mengonfigurasi sisa kode dengan tepat.

Batalkan perintah untuk operasi asinkron

Salah satu opsi terakhir untuk perintah asinkron adalah kemampuan untuk meminta perintah pembatalan yang akan dihasilkan. Ini merupakan pembungkus ICommand untuk perintah relay asinkron yang dapat digunakan untuk meminta pembatalan suatu operasi. Perintah ini akan secara otomatis memberi sinyal statusnya untuk mencerminkan apakah perintah ini dapat digunakan pada waktu tertentu atau tidak. Misalnya, jika perintah tertaut tidak dijalankan, perintah tersebut akan melaporkan statusnya karena juga tidak dapat dieksekusi. Ini dapat digunakan sebagai berikut:

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

Ini akan menyebabkan properti DoWorkCancelCommand juga akan dibuat. Ini kemudian dapat terikat ke beberapa komponen UI lain untuk dengan mudah memungkinkan pengguna membatalkan operasi asinkron yang tertunda.

Menambahkan atribut kustom

Sama seperti properti yang dapat diamati, RelayCommand generator juga menyertakan dukungan untuk atribut kustom untuk properti yang dihasilkan. Untuk memanfaatkan ini, Anda cukup menggunakan [property: ] target dalam daftar atribut melalui metode anotasi, dan Toolkit MVVM akan meneruskan atribut tersebut ke properti perintah yang dihasilkan.

Misalnya, pertimbangkan metode seperti ini:

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

Ini akan menghasilkan properti GreetUserCommand, dengan atribut [JsonIgnore] di atasnya. Anda dapat menggunakan sebanyak mungkin daftar atribut yang menargetkan metode, dan semuanya akan diteruskan ke properti yang dihasilkan.

Examples

  • Lihat contoh aplikasi (untuk beberapa kerangka kerja UI) untuk melihat Toolkit MVVM beraksi.
  • Anda juga dapat menemukan lebih banyak contoh dalam pengujian unit.