Bagikan melalui


Atribut RelayCommand

Jenisnya RelayCommand adalah atribut yang memungkinkan pembuatan properti perintah relai untuk metode anotasi. Tujuannya adalah untuk sepenuhnya menghilangkan boilerplate yang diperlukan untuk menentukan perintah yang membungkus metode privat dalam viewmodel.

Catatan

Untuk bekerja, metode anotasi harus berada di kelas parsial. Jika jenis ditumpuk, semua jenis dalam pohon sintaksis deklarasi juga harus dianotasikan sebagai parsial. 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>, , TaskCancellationToken

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);

Catatan

Nama perintah yang dihasilkan akan dibuat berdasarkan nama metode. Generator akan menggunakan nama metode dan menambahkan "Perintah" di akhir, dan akan menghapus awalan "Aktif", jika ada. Selain itu, untuk metode asinkron, akhiran "Asinkron" juga dilucuti sebelum "Perintah" disempurnakan.

Parameter perintah

Atribut [RelayCommand] mendukung pembuatan perintah untuk metode dengan parameter . Dalam hal ini, perintah akan secara otomatis mengubah perintah yang dihasilkan menjadi sebagai IRelayCommand<T> gantinya, menerima parameter dengan jenis 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 ini [RelayCommand] juga mendukung pembungkusan metode asinkron, melalui IAsyncRelayCommand antarmuka dan IAsyncRelayCommand<T> . Ini ditangani secara otomatis setiap kali metode mengembalikan Task jenis. 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 akan disebarluaskan ke perintah untuk mengaktifkan 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 menghasilkan perintah yang dihasilkan meneruskan token ke metode yang dibungkus. Ini memungkinkan konsumen untuk hanya memanggil IAsyncRelayCommand.Cancel untuk memberi sinyal token itu, dan untuk memungkinkan operasi yang tertunda 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 Button pengikatan kontrol ke GreetUserCommand, yang berarti setiap kali peristiwanya CanExecuteChanged dinaikkan, UI akan memanggil metodenya CanExecute lagi. Ini akan menyebabkan metode yang CanGreetUser dibungkus dievaluasi, yang akan mengembalikan status baru untuk tombol berdasarkan apakah instans input User atau tidak (yang di UI terikat ke SelectedUser properti) adalah 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.

Catatan

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 menumbuhkan kembali (default): ketika perintah menunggu penyelesaian pemanggilan, pengecualian apa pun secara alami akan dilemparkan pada 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).
  • Pengecualian alur ke penjadwal tugas: jika perintah dikonfigurasi untuk mengalirkan pengecualian ke penjadwal tugas, pengecualian yang dilemparkan tidak akan merusak aplikasi, tetapi sebaliknya keduanya akan tersedia melalui yang diekspos IAsyncRelayCommand.ExecutionTask serta menggelegak 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 default adalah memiliki perintah menunggu dan menumbuhkan 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 ini juga akan menyebabkan pengecualian lain yang tidak terkait tidak ditumbuhi kembali secara otomatis, jadi Anda harus dengan hati-hati memutuskan cara mendekati setiap skenario individu 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 adalah ICommand membungkus perintah relai asinkron yang dapat digunakan untuk meminta pembatalan 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 DoWorkCancelCommand properti juga dihasilkan. 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 GreetUserCommand properti, dengan atribut di [JsonIgnore] atasnya. Anda dapat menggunakan daftar atribut sebanyak yang menargetkan metode seperti yang Anda inginkan, dan semuanya akan diteruskan ke properti yang dihasilkan.

Contoh