ObservableObject

ObservableObject adalah kelas dasar untuk objek yang dapat diamati dengan mengimplementasikan INotifyPropertyChanged antarmuka dan INotifyPropertyChanging . Ini dapat digunakan sebagai titik awal untuk semua jenis objek yang perlu mendukung pemberitahuan perubahan properti.

API Platform:ObservableObject, TaskNotifier, TaskNotifier<T>

Cara kerjanya

ObservableObject memiliki fitur utama berikut:

  • Ini menyediakan implementasi dasar untuk INotifyPropertyChanged dan INotifyPropertyChanging, serta mengekspos peristiwa PropertyChanged dan PropertyChanging.
  • Ini menyediakan serangkaian metode SetProperty yang dapat digunakan untuk mengatur nilai properti pada tipe yang mewarisi ObservableObject dengan mudah, dan untuk secara otomatis memicu event yang sesuai.
  • Metode SetPropertyAndNotifyOnCompletion disediakan, yang serupa dengan SetProperty tetapi memiliki kemampuan untuk menetapkan properti Task dan memicu event notifikasi secara otomatis saat tugas yang ditetapkan selesai.
  • Ini mengekspos metode OnPropertyChanged dan OnPropertyChanging, yang dapat dioverride dalam tipe turunan untuk menyesuaikan cara event notifikasi dipicu.

Properti sederhana

Berikut adalah contoh cara menerapkan dukungan pemberitahuan ke properti kustom:

public class User : ObservableObject
{
    private string name;

    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }
}

Metode yang disediakan SetProperty<T>(ref T, T, string) memeriksa nilai properti saat ini, dan memperbaruinya jika berbeda, lalu juga menaikkan peristiwa yang relevan secara otomatis. Nama properti secara otomatis diambil melalui penggunaan [CallerMemberName] atribut, jadi tidak perlu menentukan properti mana yang sedang diperbarui secara manual.

Membungkus model yang tidak dapat diamati

Skenario yang umum, misalnya saat bekerja dengan item basis data, adalah membuat model pembungkus yang "bindable" yang meneruskan properti-properti model basis data dan memicu notifikasi perubahan properti saat diperlukan. Ini juga diperlukan ketika ingin menyuntikkan dukungan pemberitahuan ke model, yang tidak mengimplementasikan INotifyPropertyChanged antarmuka. ObservableObject menyediakan metode khusus untuk membuat proses ini lebih sederhana. Untuk contoh berikut, User adalah model yang langsung memetakan tabel database, tanpa mewarisi dari ObservableObject:

public class ObservableUser : ObservableObject
{
    private readonly User user;

    public ObservableUser(User user) => this.user = user;

    public string Name
    {
        get => user.Name;
        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
    }
}

Dalam hal ini kita menggunakan SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) kelebihan beban. Tanda tangan sedikit lebih kompleks daripada yang sebelumnya - ini perlu untuk membiarkan kode masih sangat efisien bahkan jika kita tidak memiliki akses ke bidang dukungan seperti dalam skenario sebelumnya. Kita dapat melalui setiap bagian dari tanda tangan metode ini secara rinci untuk memahami peran komponen yang berbeda:

  • TModel adalah argumen jenis, menunjukkan jenis model yang kita bungkus. Dalam kasus ini, ini adalah kelas User kita. Perhatikan bahwa kita tidak perlu menentukan ini secara eksplisit - pengkompilasi C# akan menyimpulkan ini secara otomatis dengan cara kita memanggil metode .SetProperty
  • T adalah jenis properti yang ingin kita tetapkan. Seperti halnya TModel, hal ini disimpulkan secara otomatis.
  • T oldValue adalah parameter pertama, dan dalam hal ini kita menggunakan user.Name untuk meneruskan nilai saat ini dari properti yang sedang kita bungkus.
  • T newValue adalah nilai baru yang ditetapkan ke properti tersebut, dan di sini kita meneruskan value, yang merupakan nilai masukan di dalam setter properti.
  • TModel model adalah model target yang kita bungkus; dalam hal ini, kita meneruskan instans yang disimpan dalam field user.
  • Action<TModel, T> callback adalah fungsi yang akan dipanggil jika nilai baru properti berbeda dari yang saat ini, dan properti perlu diatur. Ini akan dilakukan oleh fungsi callback ini, yang menerima model target dan nilai properti baru yang akan ditetapkan sebagai masukan. Dalam hal ini kami hanya menetapkan nilai input (yang kami panggil n) ke Name properti (dengan melakukan u.Name = n). Penting di sini untuk menghindari pengambilan nilai dari cakupan saat ini dan hanya berinteraksi dengan yang diberikan sebagai input ke panggilan balik, karena ini memungkinkan pengkompilasi C# untuk menyimpan fungsi panggilan balik dan melakukan sejumlah peningkatan performa. Karena itulah kita tidak langsung mengakses field user di sini atau parameter value di setter, melainkan hanya menggunakan parameter masukan untuk ekspresi lambda.

Metode ini SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) membuat pembuatan properti pembungkusan ini sangat sederhana, karena mengurus pengambilan dan pengaturan properti target sambil menyediakan API yang sangat ringkas.

Catatan

Dibandingkan dengan implementasi metode ini menggunakan ekspresi LINQ, khususnya melalui parameter jenis Expression<Func<T>> alih-alih parameter status dan panggilan balik, peningkatan performa yang dapat dicapai dengan cara ini benar-benar signifikan. Secara khusus, versi ini ~ 200x lebih cepat daripada yang menggunakan ekspresi LINQ, dan tidak membuat alokasi memori sama sekali.

Menangani Task<T> properti

Jika sebuah properti adalah Task, maka peristiwa notifikasi juga perlu dipicu setelah tugas selesai dijalankan, agar binding diperbarui pada waktu yang tepat. Misalnya, untuk menampilkan indikator pemuatan atau informasi status lainnya untuk operasi yang direpresentasikan oleh tugas tersebut. ObservableObject memiliki API untuk skenario ini:

public class MyModel : ObservableObject
{
    private TaskNotifier<int>? requestTask;

    public Task<int>? RequestTask
    {
        get => requestTask;
        set => SetPropertyAndNotifyOnCompletion(ref requestTask, value);
    }

    public void RequestValue()
    {
        RequestTask = WebService.LoadMyValueAsync();
    }
}

SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string) Di sini metode akan mengurus pembaruan bidang target, memantau tugas baru, jika ada, dan menaikkan peristiwa pemberitahuan ketika tugas tersebut selesai. Dengan demikian, Anda cukup mengikatkan ke properti tugas dan mendapat notifikasi saat statusnya berubah. TaskNotifier<T> adalah jenis khusus yang diekspos oleh ObservableObject yang membungkus instans target Task<T> dan memungkinkan logika pemberitahuan yang diperlukan untuk metode ini. Tipe TaskNotifier juga dapat digunakan secara langsung jika Anda hanya memiliki Task umum.

Catatan

Metode SetPropertyAndNotifyOnCompletion ini dimaksudkan untuk mengganti penggunaan jenis NotifyTaskCompletion<T> dari Microsoft.Toolkit paket. Jika tipe ini digunakan, tipe ini dapat diganti dengan hanya menggunakan properti internal Task (atau Task<TResult>), lalu metode SetPropertyAndNotifyOnCompletion dapat digunakan untuk menetapkan nilainya dan memicu notifikasi perubahan. Semua properti yang diekspos oleh tipe NotifyTaskCompletion<T> tersedia langsung pada instans Task.

Contoh

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