Bagikan melalui


Properti dependensi kustom (WPF .NET)

Pengembang aplikasi dan penulis komponen Windows Presentation Foundation (WPF) dapat membuat properti dependensi kustom untuk memperluas fungsionalitas properti mereka. Tidak seperti properti runtime bahasa umum (CLR), properti dependensi menambahkan dukungan untuk gaya, pengikatan data, pewarisan, animasi, dan nilai default. Background, Width, dan Text adalah contoh properti dependensi yang ada di kelas WPF. Artikel ini menjelaskan cara menerapkan properti dependensi kustom, dan menyajikan opsi untuk meningkatkan performa, kegunaan, dan fleksibilitas.

Penting

Dokumentasi Panduan Desktop untuk .NET 7 dan .NET 6 sedang dibangun.

Prasyarat

Artikel ini mengasumsikan pengetahuan dasar tentang properti dependensi, dan bahwa Anda telah membaca gambaran umum properti Dependensi. Untuk mengikuti contoh dalam artikel ini, ini membantu jika Anda terbiasa dengan Extensible Application Markup Language (XAML) dan tahu cara menulis aplikasi WPF.

Pengidentifikasi properti dependensi

Properti dependensi adalah properti yang terdaftar dengan sistem properti WPF melalui Register atau RegisterReadOnly panggilan. Metode ini Register mengembalikan instans DependencyProperty yang menyimpan nama terdaftar dan karakteristik properti dependensi. Anda akan menetapkan DependencyProperty instans ke bidang readonly statis, yang dikenal sebagai pengidentifikasi properti dependensi, yang berdasarkan konvensi diberi nama <property name>Property. Misalnya, bidang pengidentifikasi untuk Background properti selalu BackgroundProperty.

Pengidentifikasi properti dependensi digunakan sebagai bidang dukungan untuk mendapatkan atau mengatur nilai properti, bukan pola standar mendukung properti dengan bidang privat. Sistem properti tidak hanya menggunakan pengidentifikasi, prosesor XAML dapat menggunakannya, dan kode Anda (dan mungkin kode eksternal) dapat mengakses properti dependensi melalui pengidentifikasinya.

Properti dependensi hanya dapat diterapkan ke kelas yang berasal dari DependencyObject jenis. Sebagian besar kelas WPF mendukung properti dependensi, karena DependencyObject dekat dengan akar hierarki kelas WPF. Untuk informasi selengkapnya tentang properti dependensi, dan terminologi dan konvensi yang digunakan untuk menjelaskannya, lihat Gambaran umum properti dependensi.

Pembungkus properti dependensi

Properti dependensi WPF yang tidak terpasang properti diekspos oleh pembungkus get CLR yang mengimplementasikan dan set pengakses. Dengan menggunakan pembungkus properti, konsumen properti dependensi bisa mendapatkan atau mengatur nilai properti dependensi, sama seperti properti CLR lainnya. Aksesor get dan set berinteraksi dengan sistem properti yang mendasar melalui DependencyObject.GetValue dan DependencyObject.SetValue panggilan, meneruskan pengidentifikasi properti dependensi sebagai parameter. Konsumen properti dependensi biasanya tidak menelepon GetValue atau SetValue secara langsung, tetapi jika Anda menerapkan properti dependensi kustom, Anda akan menggunakan metode tersebut dalam pembungkus.

Kapan menerapkan properti dependensi

Saat Anda menerapkan properti pada kelas yang berasal dari DependencyObject, Anda menjadikannya properti dependensi dengan mendukung properti Anda dengan DependencyProperty pengidentifikasi. Apakah bermanfaat untuk membuat properti dependensi tergantung pada skenario Anda. Meskipun mendukung properti Anda dengan bidang privat memadai untuk beberapa skenario, pertimbangkan untuk menerapkan properti dependensi jika Anda ingin properti Anda mendukung satu atau beberapa kemampuan WPF berikut:

  • Properti yang dapat diatur dalam gaya. Untuk informasi lebih lanjut, lihat Gaya dan template.

  • Properti yang mendukung pengikatan data. Untuk informasi selengkapnya tentang properti dependensi pengikatan data, lihat Mengikat properti dua kontrol.

  • Properti yang dapat diatur melalui referensi sumber daya dinamis. Untuk informasi selengkapnya, lihat sumber daya XAML.

  • Properti yang secara otomatis mewarisi nilainya dari elemen induk di pohon elemen. Untuk ini, Anda harus mendaftar menggunakan RegisterAttached, bahkan jika Anda juga membuat pembungkus properti untuk akses CLR. Untuk informasi selengkapnya, lihat Pewarisan nilai properti.

  • Properti yang dapat dianimasikan. Untuk informasi lebih lanjut, lihat Gambaran Umum animasi.

  • Pemberitahuan oleh sistem properti WPF saat nilai properti berubah. Perubahan mungkin disebabkan oleh tindakan oleh sistem properti, lingkungan, pengguna, atau gaya. Properti Anda dapat menentukan metode panggilan balik dalam metadata properti yang akan dipanggil setiap kali sistem properti menentukan bahwa nilai properti Anda berubah. Konsep terkait adalah koersi nilai properti. Untuk informasi selengkapnya, lihat Panggilan balik dan validasi properti dependensi.

  • Akses ke metadata properti dependensi, yang dibaca oleh proses WPF. Misalnya, Anda dapat menggunakan metadata properti untuk:

    • Tentukan apakah nilai properti dependensi yang diubah harus membuat sistem tata letak menyusun ulang visual untuk elemen.

    • Atur nilai default properti dependensi, dengan menimpa metadata pada kelas turunan.

  • Dukungan perancang Visual Studio WPF, seperti mengedit properti kontrol kustom di jendela Properti . Untuk informasi selengkapnya, lihat Gambaran umum penulisan kontrol.

Untuk beberapa skenario, mengambil alih metadata properti dependensi yang ada adalah opsi yang lebih baik daripada menerapkan properti dependensi baru. Apakah penimpaan metadata praktis tergantung pada skenario Anda, dan seberapa dekat skenario tersebut menyerupai implementasi properti dan kelas dependensi WPF yang ada. Untuk informasi selengkapnya tentang mengganti metadata pada properti dependensi yang ada, lihat Metadata properti dependensi.

Daftar periksa untuk membuat properti dependensi

Ikuti langkah-langkah ini untuk membuat properti dependensi. Beberapa langkah dapat digabungkan dan diimplementasikan dalam satu baris kode.

  1. (Opsional) Buat metadata properti dependensi.

  2. Daftarkan properti dependensi dengan sistem properti, menentukan nama properti, jenis pemilik, jenis nilai properti, dan secara opsional, metadata properti.

  3. DependencyProperty Tentukan pengidentifikasi sebagai public static readonly bidang pada jenis pemilik. Nama bidang pengidentifikasi Property adalah nama properti dengan akhiran ditambahkan.

  4. Tentukan properti pembungkus CLR dengan nama yang sama dengan nama properti dependensi. Dalam pembungkus CLR, terapkan get dan set aksesor yang terhubung dengan properti dependensi yang mendukung pembungkus.

Mendaftarkan properti

Agar properti Anda menjadi properti dependensi, Anda harus mendaftarkannya dengan sistem properti. Untuk mendaftarkan properti Anda, panggil Register metode dari dalam isi kelas Anda, tetapi di luar definisi anggota apa pun. Metode ini Register mengembalikan pengidentifikasi properti dependensi unik yang akan Anda gunakan saat memanggil API sistem properti. Alasan bahwa Register panggilan dilakukan di luar definisi anggota adalah karena Anda menetapkan nilai pengembalian ke public static readonly bidang jenis DependencyProperty. Bidang ini, yang akan Anda buat di kelas Anda, adalah pengidentifikasi untuk properti dependensi Anda. Dalam contoh berikut, argumen pertama nama Register properti AquariumGraphicdependensi .

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

Catatan

Menentukan properti dependensi dalam isi kelas adalah implementasi umum, tetapi juga dimungkinkan untuk menentukan properti dependensi di konstruktor statis kelas. Pendekatan ini mungkin masuk akal jika Anda memerlukan lebih dari satu baris kode untuk menginisialisasi properti dependensi.

Penamaan properti dependensi

Konvensi penamaan yang ditetapkan untuk properti dependensi wajib untuk perilaku normal sistem properti. Nama bidang pengidentifikasi Propertyyang Anda buat harus berupa nama properti terdaftar dengan akhiran .

Nama properti dependensi harus unik dalam kelas pendaftaran. Properti dependensi yang diwarisi melalui jenis dasar telah didaftarkan, dan tidak dapat didaftarkan oleh jenis turunan. Namun, Anda dapat menggunakan properti dependensi yang didaftarkan oleh jenis yang berbeda, bahkan jenis yang tidak diwarisi kelas Anda, dengan menambahkan kelas Anda sebagai pemilik properti dependensi. Untuk informasi selengkapnya tentang menambahkan kelas sebagai pemilik, lihat Metadata properti dependensi.

Menerapkan pembungkus properti

Menurut konvensi, nama properti pembungkus harus sama dengan parameter Register pertama panggilan, yang merupakan nama properti dependensi. Implementasi pembungkus Anda akan memanggil GetValue aksesor get , dan SetValue di set aksesor (untuk properti baca-tulis). Contoh berikut menunjukkan pembungkus—mengikuti panggilan pendaftaran dan deklarasi bidang pengidentifikasi. Semua properti dependensi publik pada kelas WPF menggunakan model pembungkus serupa.

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );

// Declare a read-write property wrapper.
public Uri AquariumGraphic
{
    get => (Uri)GetValue(AquariumGraphicProperty);
    set => SetValue(AquariumGraphicProperty, value);
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

' Declare a read-write property wrapper.
Public Property AquariumGraphic As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set
        SetValue(AquariumGraphicProperty, Value)
    End Set
End Property

Kecuali dalam kasus yang jarang terjadi, implementasi pembungkus Anda hanya boleh berisi GetValue kode dan SetValue . Untuk alasan di balik ini, lihat Implikasi untuk properti dependensi kustom.

Jika properti Anda tidak mengikuti konvensi penamaan yang ditetapkan, Anda mungkin mengalami masalah ini:

  • Beberapa aspek gaya dan templat tidak akan berfungsi.

  • Sebagian besar alat dan desainer mengandalkan konvensi penamaan untuk menserialisasikan XAML dengan benar dan memberikan bantuan lingkungan perancang pada tingkat per properti.

  • Implementasi pemuat WPF XAML saat ini melewati pembungkus sepenuhnya, dan bergantung pada konvensi penamaan untuk memproses nilai atribut. Untuk informasi selengkapnya, lihat properti pemuatan dan dependensi XAML.

Metadata properti dependensi

Saat Anda mendaftarkan properti dependensi, sistem properti membuat objek metadata untuk menyimpan karakteristik properti. Kelebihan beban Register metode memungkinkan Anda menentukan metadata properti selama pendaftaran, misalnya Register(String, Type, Type, PropertyMetadata). Penggunaan umum metadata properti adalah menerapkan nilai default kustom untuk instans baru yang menggunakan properti dependensi. Jika Anda tidak menyediakan metadata properti, sistem properti akan menetapkan nilai default ke banyak karakteristik properti dependensi.

Jika Anda membuat properti dependensi pada kelas yang berasal dari FrameworkElement, Anda dapat menggunakan kelas FrameworkPropertyMetadata metadata yang lebih khusus daripada kelas PropertyMetadatadasarnya . Beberapa FrameworkPropertyMetadata tanda tangan konstruktor memungkinkan Anda menentukan kombinasi karakteristik metadata yang berbeda. Jika Anda hanya ingin menentukan nilai default, gunakan FrameworkPropertyMetadata(Object) dan teruskan nilai default ke Object parameter . Pastikan bahwa jenis nilai cocok dengan yang propertyType ditentukan dalam Register panggilan.

Beberapa FrameworkPropertyMetadata kelebihan beban memungkinkan Anda menentukan bendera opsi metadata untuk properti Anda. Sistem properti mengonversi bendera ini menjadi properti diskrit dan nilai bendera digunakan oleh proses WPF, seperti mesin tata letak.

Mengatur bendera metadata

Pertimbangkan hal berikut saat mengatur bendera metadata:

  • Jika nilai properti Anda (atau perubahannya) memengaruhi cara sistem tata letak merender elemen UI, maka atur satu atau beberapa bendera berikut:

    • AffectsMeasure, yang menunjukkan bahwa perubahan nilai properti memerlukan perubahan dalam penyajian UI, khususnya ruang yang ditempati oleh objek dalam induknya. Misalnya, atur bendera metadata ini untuk Width properti.

    • AffectsArrange, yang menunjukkan bahwa perubahan nilai properti memerlukan perubahan dalam penyajian UI, khususnya posisi objek dalam induknya. Biasanya, objek tidak juga mengubah ukuran. Misalnya, atur bendera metadata ini untuk Alignment properti .

    • AffectsRender, yang menunjukkan bahwa perubahan telah terjadi yang tidak memengaruhi tata letak dan pengukuran, tetapi masih memerlukan render lain. Misalnya, atur bendera ini untuk Background properti, atau properti lain yang memengaruhi warna elemen.

    Anda juga dapat menggunakan bendera ini sebagai input untuk mengambil alih implementasi panggilan balik sistem properti (atau tata letak). Misalnya, Anda dapat menggunakan OnPropertyChanged panggilan balik untuk memanggil InvalidateArrange saat properti instans melaporkan perubahan nilai dan telah AffectsArrange diatur dalam metadata.

  • Beberapa properti memengaruhi karakteristik penyajian elemen induknya dengan cara lain. Misalnya, perubahan pada MinOrphanLines properti dapat mengubah penyajian keseluruhan dokumen alur. Gunakan AffectsParentArrange atau AffectsParentMeasure untuk memberi sinyal tindakan induk di properti Anda sendiri.

  • Secara default, properti dependensi mendukung pengikatan data. Namun, Anda dapat menggunakan IsDataBindingAllowed untuk menonaktifkan pengikatan data ketika tidak ada skenario realistis untuk itu, atau di mana performa pengikatan data bermasalah, seperti pada objek besar.

  • Meskipun mode pengikatan data default untuk properti dependensi adalah OneWay, Anda dapat mengubah mode pengikatan pengikatan tertentu menjadi TwoWay. Untuk informasi selengkapnya, lihat Arah pengikatan. Sebagai penulis properti dependensi, Anda bahkan dapat memilih untuk membuat pengikatan dua arah mode default. Contoh properti dependensi yang ada yang menggunakan pengikatan data dua arah adalah MenuItem.IsSubmenuOpen, yang memiliki status yang didasarkan pada properti lain dan panggilan metode. Skenario untuk IsSubmenuOpen adalah bahwa logika pengaturannya, dan MenuItempembuatan , berinteraksi dengan gaya tema default. TextBox.Text adalah properti dependensi WPF lain yang menggunakan pengikatan dua arah secara default.

  • Anda dapat mengaktifkan pewarisan properti untuk properti dependensi Anda dengan mengatur Inherits bendera. Pewarisan properti berguna untuk skenario di mana elemen induk dan anak memiliki properti yang sama dan masuk akal bagi elemen turunan untuk mewarisi nilai induk untuk properti umum. Contoh properti yang dapat diwariskan adalah DataContext, yang mendukung operasi pengikatan yang menggunakan skenario detail master untuk presentasi data. Pewarisan nilai properti memungkinkan Anda menentukan konteks data di halaman atau akar aplikasi, yang menyimpan harus menentukannya untuk pengikatan elemen turunan. Meskipun nilai properti yang diwariskan mengambil alih nilai default, nilai properti dapat diatur secara lokal pada elemen turunan apa pun. Gunakan pewarisan nilai properti dengan hemat karena memiliki biaya performa. Untuk informasi selengkapnya, lihat Pewarisan nilai properti.

  • Atur Journal bendera untuk menunjukkan bahwa properti dependensi Anda harus terdeteksi atau digunakan oleh layanan jurnal navigasi. Misalnya, SelectedIndex properti mengatur Journal bendera untuk merekomendasikan agar aplikasi menyimpan riwayat penjurnalan item yang dipilih.

Properti dependensi baca-saja

Anda dapat menentukan properti dependensi yang bersifat baca-saja. Skenario umum adalah properti dependensi yang menyimpan status internal. Misalnya, IsMouseOver bersifat baca-saja karena statusnya hanya boleh ditentukan oleh input mouse. Untuk informasi selengkapnya, lihat Properti dependensi baca-saja.

Properti dependensi jenis kumpulan

Properti dependensi jenis koleksi memiliki masalah implementasi tambahan yang perlu dipertimbangkan, seperti mengatur nilai default untuk jenis referensi dan dukungan pengikatan data untuk elemen pengumpulan. Untuk informasi selengkapnya, lihat Properti dependensi jenis koleksi.

Keamanan properti dependensi

Biasanya, Anda akan mendeklarasikan properti dependensi sebagai properti publik, dan DependencyProperty bidang pengidentifikasi sebagai public static readonly bidang. Jika Anda menentukan tingkat akses yang lebih ketat, seperti protected, properti dependensi masih dapat diakses melalui pengidentifikasinya dalam kombinasi dengan API sistem properti. Bahkan bidang pengidentifikasi yang dilindungi berpotensi dapat diakses melalui pelaporan metadata WPF atau API penentuan nilai, seperti LocalValueEnumerator. Untuk informasi selengkapnya, lihat Keamanan properti dependensi.

Untuk properti dependensi baca-saja, nilai yang dikembalikan adalah RegisterReadOnlyDependencyPropertyKey, dan biasanya Anda tidak akan menjadi DependencyPropertyKeypublic anggota kelas Anda. Karena sistem properti WPF tidak menyebarluaskan DependencyPropertyKey bagian luar kode Anda, properti dependensi baca-saja memiliki keamanan yang lebih baik set daripada properti dependensi baca-tulis.

Properti dependensi dan konstruktor kelas

Ada prinsip umum dalam pemrograman kode terkelola, sering diberlakukan oleh alat analisis kode, bahwa konstruktor kelas tidak boleh memanggil metode virtual. Ini karena konstruktor dasar dapat dipanggil selama inisialisasi konstruktor kelas turunan, dan metode virtual yang disebut oleh konstruktor dasar mungkin berjalan sebelum inisialisasi lengkap kelas turunan. Ketika Anda berasal dari kelas yang sudah berasal dari DependencyObject, sistem properti itu sendiri memanggil dan mengekspos metode virtual secara internal. Metode virtual ini adalah bagian dari layanan sistem properti WPF. Mengambil alih metode memungkinkan kelas turunan untuk berpartisipasi dalam penentuan nilai. Untuk menghindari potensi masalah dengan inisialisasi runtime, Anda tidak boleh mengatur nilai properti dependensi dalam konstruktor kelas, kecuali Anda mengikuti pola konstruktor tertentu. Untuk informasi selengkapnya, lihat Brankas pola konstruktor untuk DependencyObjects.

Baca juga