Bagikan melalui


Properti Dependensi Kustom

Topik ini menjelaskan alasan bahwa pengembang aplikasi Windows Presentation Foundation (WPF) dan penulis komponen mungkin ingin membuat properti dependensi kustom, dan menjelaskan langkah-langkah implementasi serta beberapa opsi implementasi yang dapat meningkatkan performa, kegunaan, atau fleksibilitas properti.

Prasyarat

Topik ini mengasumsikan bahwa Anda memahami properti dependensi dari perspektif konsumen properti dependensi yang ada pada kelas WPF, dan telah membaca topik Gambaran Umum Properti Dependensi. Untuk mengikuti contoh dalam topik ini, Anda juga harus memahami XAML dan tahu cara menulis aplikasi WPF.

Apa itu Properti Dependensi?

Anda dapat mengaktifkan apa yang akan menjadi properti runtime bahasa umum (CLR) untuk mendukung gaya, pengikatan data, pewarisan, animasi, dan nilai default dengan menerapkannya sebagai properti dependensi. Properti dependensi adalah properti yang terdaftar dengan sistem properti WPF dengan memanggil Register metode (atau RegisterReadOnly), dan yang didukung oleh DependencyProperty bidang pengidentifikasi. Properti dependensi hanya dapat digunakan berdasarkan DependencyObject jenis, tetapi DependencyObject cukup tinggi dalam hierarki kelas WPF, sehingga sebagian besar kelas yang tersedia di WPF dapat mendukung properti dependensi. Untuk informasi selengkapnya tentang properti dependensi dan beberapa terminologi dan konvensi yang digunakan untuk menjelaskannya dalam SDK ini, lihat Gambaran Umum Properti Dependensi.

Contoh Properti Dependensi

Contoh properti dependensi yang diimplementasikan pada kelas WPF termasuk Background properti, Width properti , dan Text properti , di antara banyak lainnya. Setiap properti dependensi yang diekspos oleh kelas memiliki bidang statis publik yang sesuai dari jenis DependencyProperty yang diekspos pada kelas yang sama. Ini adalah pengidentifikasi untuk properti dependensi. Pengidentifikasi diberi nama menggunakan konvensi: nama properti dependensi dengan string Property yang ditambahkan ke dalamnya. Misalnya, bidang pengidentifikasi yang DependencyProperty sesuai untuk Background properti adalah BackgroundProperty. Pengidentifikasi menyimpan informasi tentang properti dependensi seperti yang terdaftar, dan pengidentifikasi kemudian digunakan nanti untuk operasi lain yang melibatkan properti dependensi, seperti memanggil SetValue.

Seperti disebutkan dalam Gambaran Umum Properti Dependensi, semua properti dependensi di WPF (kecuali properti yang paling terlampir) juga merupakan properti CLR karena implementasi "pembungkus". Oleh karena itu, dari kode, Anda bisa mendapatkan atau mengatur properti dependensi dengan memanggil aksesor CLR yang menentukan pembungkus dengan cara yang sama seperti Anda akan menggunakan properti CLR lainnya. Sebagai konsumen properti dependensi yang ditetapkan, Anda biasanya tidak menggunakan DependencyObject metode dan SetValue, yang merupakan titik koneksi ke sistem properti yang mendasarGetValue. Sebaliknya, implementasi properti CLR yang ada akan sudah memanggil GetValue dan SetValue dalam get implementasi dan set pembungkus properti, menggunakan bidang pengidentifikasi dengan tepat. Jika Anda menerapkan properti dependensi kustom sendiri, maka Anda akan menentukan pembungkus dengan cara yang sama.

Kapan Anda Harus Menerapkan Properti Dependensi?

Ketika Anda menerapkan properti pada kelas, selama kelas Anda berasal dari DependencyObject, Anda memiliki opsi untuk mendukung properti Anda dengan DependencyProperty pengidentifikasi dan dengan demikian menjadikannya properti dependensi. Memiliki properti Anda menjadi properti dependensi tidak selalu diperlukan atau sesuai, dan akan bergantung pada kebutuhan skenario Anda. Terkadang, teknik khas mendukung properti Anda dengan bidang privat memadai. Namun, Anda harus menerapkan properti Anda sebagai properti dependensi kapan pun Anda ingin properti Anda mendukung satu atau beberapa kemampuan WPF berikut:

  • Anda ingin properti Anda dapat diatur dalam gaya. Untuk informasi selengkapnya, lihat Gaya dan Templat.

  • Anda ingin properti Anda mendukung pengikatan data. Untuk informasi selengkapnya tentang properti dependensi pengikatan data, lihat Mengikat Properti Dua Kontrol.

  • Anda ingin properti Anda diatur dengan referensi sumber daya dinamis. Untuk informasi selengkapnya, lihat Sumber Daya XAML.

  • Anda ingin mewarisi nilai properti secara otomatis dari elemen induk di pohon elemen. Dalam hal ini, daftar dengan RegisterAttached metode , bahkan jika Anda juga membuat pembungkus properti untuk akses CLR. Untuk informasi selengkapnya, lihat Pewarisan Nilai Properti.

  • Anda ingin properti Anda dapat dianimasikan. Untuk informasi selengkapnya, lihat gambaran umum Animation.

  • Anda ingin sistem properti melaporkan ketika nilai properti sebelumnya telah diubah oleh tindakan yang diambil oleh sistem properti, lingkungan, atau pengguna, atau dengan membaca dan menggunakan gaya. Dengan menggunakan metadata properti, properti Anda dapat menentukan metode panggilan balik yang akan dipanggil setiap kali sistem properti menentukan bahwa nilai properti Anda diubah secara definitif. Konsep terkait adalah koersi nilai properti. Untuk informasi selengkapnya, lihat Panggilan Balik dan Validasi Properti Dependensi.

  • Anda ingin menggunakan konvensi metadata yang ditetapkan yang juga digunakan oleh proses WPF, seperti melaporkan apakah mengubah nilai properti harus mengharuskan sistem tata letak untuk menyusun ulang visual untuk elemen. Atau Anda ingin dapat menggunakan penimpaan metadata sehingga kelas turunan dapat mengubah karakteristik berbasis metadata seperti nilai default.

  • Anda ingin properti kontrol kustom menerima dukungan Visual Studio WPF Designer, seperti pengeditan jendela Properti . Untuk informasi selengkapnya, lihat Gambaran Umum Penulisan Kontrol.

Ketika Anda memeriksa skenario ini, Anda juga harus mempertimbangkan apakah Anda dapat mencapai skenario Anda dengan mengesampingkan metadata properti dependensi yang ada, daripada menerapkan properti yang sama sekali baru. Apakah penimpaan metadata praktis tergantung pada skenario Anda dan seberapa dekat skenario tersebut menyerupai implementasi dalam properti dan kelas dependensi WPF yang ada. Untuk informasi selengkapnya tentang mengesampingkan metadata pada properti yang ada, lihat Metadata Properti Dependensi.

Daftar periksa untuk Menentukan Properti Dependensi

Menentukan properti dependensi terdiri dari empat konsep yang berbeda. Konsep-konsep ini belum tentu merupakan langkah prosedural yang ketat, karena beberapa di antaranya akhirnya digabungkan sebagai baris kode tunggal dalam implementasi:

  • (Opsional) Buat metadata properti untuk properti dependensi.

  • Daftarkan nama properti dengan sistem properti, menentukan jenis pemilik dan jenis nilai properti. Tentukan juga metadata properti, jika digunakan.

  • DependencyProperty Tentukan pengidentifikasi sebagai publicstaticreadonly bidang pada jenis pemilik.

  • Tentukan properti "pembungkus" CLR yang namanya cocok dengan nama properti dependensi. Terapkan properti get "wrapper" CLR dan set aksesor untuk terhubung dengan properti dependensi yang mendukungnya.

Mendaftarkan Properti dengan Sistem Properti

Agar properti Anda menjadi properti dependensi, Anda harus mendaftarkan properti tersebut ke dalam tabel yang dikelola oleh sistem properti, dan memberinya pengidentifikasi unik yang digunakan sebagai kualifikasi untuk operasi sistem properti nanti. Operasi ini mungkin operasi internal, atau api sistem properti panggilan kode Anda sendiri. Untuk mendaftarkan properti, Anda memanggil Register metode dalam isi kelas Anda (di dalam kelas, tetapi di luar definisi anggota). Bidang pengidentifikasi juga disediakan oleh Register panggilan metode, sebagai nilai pengembalian. Alasan bahwa Register panggilan dilakukan di luar definisi anggota lain adalah karena Anda menggunakan nilai pengembalian ini untuk menetapkan dan membuatreadonlypublicstaticbidang jenis DependencyProperty sebagai bagian dari kelas Anda. Bidang ini menjadi pengidentifikasi untuk properti dependensi Anda.

public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))

Konvensi Nama Properti Dependensi

Ada konvensi penamaan yang ditetapkan mengenai properti dependensi yang harus Anda ikuti dalam semua keadaan tetapi luar biasa.

Properti dependensi itu sendiri akan memiliki nama dasar, "AquariumGraphic" seperti dalam contoh ini, yang diberikan sebagai parameter pertama dari Register. Nama tersebut harus unik dalam setiap jenis pendaftaran. Properti dependensi yang diwariskan melalui jenis dasar dianggap sudah menjadi bagian dari jenis pendaftaran; nama properti yang diwariskan tidak dapat didaftarkan lagi. Namun, ada teknik untuk menambahkan kelas sebagai pemilik properti dependensi bahkan ketika properti dependensi tersebut tidak diwariskan; untuk detailnya, lihat Metadata Properti Dependensi.

Saat Anda membuat bidang pengidentifikasi Property, beri nama bidang ini dengan nama properti saat Anda mendaftarkannya, ditambah akhiran . Bidang ini adalah pengidentifikasi Anda untuk properti dependensi, dan akan digunakan nanti sebagai input untuk SetValue panggilan dan GetValue yang akan Anda buat di pembungkus, oleh akses kode lain ke properti dengan kode Anda sendiri, oleh akses kode eksternal apa pun yang Anda izinkan, oleh sistem properti, dan berpotensi oleh prosesor XAML.

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.

Menerapkan "Wrapper"

Implementasi pembungkus Anda harus memanggil GetValue dalam get implementasi, dan SetValue dalam set implementasi (panggilan pendaftaran asli dan bidang juga ditampilkan di sini untuk kejelasan).

Dalam semua keadaan kecuali luar biasa, implementasi pembungkus Anda harus hanya GetValue melakukan tindakan dan SetValue , masing-masing. Alasan untuk ini dibahas dalam topik Properti Pemuatan dan Dependensi XAML.

Semua properti dependensi publik yang ada yang disediakan pada kelas WPF menggunakan model implementasi pembungkus sederhana ini; sebagian besar kompleksitas cara kerja properti dependensi secara inheren merupakan perilaku sistem properti, atau diimplementasikan melalui konsep lain seperti koersi atau panggilan balik perubahan properti melalui metadata properti.


public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
public Uri AquariumGraphic
{
  get { return (Uri)GetValue(AquariumGraphicProperty); }
  set { SetValue(AquariumGraphicProperty, value); }
}

Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))
Public Property AquariumGraphic() As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set(ByVal value As Uri)
        SetValue(AquariumGraphicProperty, value)
    End Set
End Property

Sekali lagi, menurut konvensi, nama properti pembungkus harus sama dengan nama yang dipilih dan diberikan sebagai parameter pertama panggilan Register yang mendaftarkan properti. Jika properti Anda tidak mengikuti konvensi, ini tidak selalu menonaktifkan semua kemungkinan penggunaan, tetapi Anda akan mengalami beberapa masalah penting:

  • Aspek gaya dan templat tertentu tidak akan berfungsi.

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

  • Implementasi pemuat WPF XAML saat ini melewati pembungkus sepenuhnya, dan bergantung pada konvensi penamaan saat memproses nilai atribut. Untuk informasi selengkapnya, lihat Properti Pemuatan dan Dependensi XAML.

Metadata Properti untuk Properti Dependensi Baru

Saat Anda mendaftarkan properti dependensi, pendaftaran melalui sistem properti membuat objek metadata yang menyimpan karakteristik properti. Banyak dari karakteristik ini memiliki default yang diatur jika properti terdaftar dengan tanda tangan sederhana .Register Tanda tangan Register lain memungkinkan Anda menentukan metadata yang Anda inginkan saat mendaftarkan properti. Metadata paling umum yang diberikan untuk properti dependensi adalah memberi mereka nilai default yang diterapkan pada instans baru yang menggunakan properti .

Jika Anda membuat properti dependensi yang ada pada kelas turunan FrameworkElement, Anda dapat menggunakan kelas FrameworkPropertyMetadata metadata yang lebih khusus daripada kelas dasar PropertyMetadata . Konstruktor untuk FrameworkPropertyMetadata kelas memiliki beberapa tanda tangan di mana Anda dapat menentukan berbagai karakteristik metadata dalam kombinasi. Jika Anda hanya ingin menentukan nilai default, gunakan tanda tangan yang mengambil satu parameter jenis Object. Teruskan parameter objek tersebut sebagai nilai default khusus jenis untuk properti Anda (nilai default yang disediakan harus berupa jenis yang Anda berikan sebagai propertyType parameter dalam Register panggilan).

Untuk FrameworkPropertyMetadata, Anda juga dapat menentukan bendera opsi metadata untuk properti Anda. Bendera ini dikonversi menjadi properti diskrit pada metadata properti setelah pendaftaran dan digunakan untuk mengomunikasikan kondisional tertentu ke proses lain seperti mesin tata letak.

Mengatur Bendera Metadata yang Sesuai

  • Jika properti Anda (atau perubahan nilainya) memengaruhi antarmuka pengguna (UI), dan khususnya memengaruhi bagaimana sistem tata letak harus mengukur atau merender elemen Anda di halaman, atur satu atau beberapa bendera berikut: AffectsMeasure, , AffectsArrangeAffectsRender.

    • AffectsMeasure menunjukkan bahwa perubahan pada properti ini memerlukan perubahan pada penyajian UI di mana objek yang berisi mungkin memerlukan lebih banyak atau kurang ruang dalam induk. Misalnya, properti "Lebar" harus memiliki set bendera ini.

    • AffectsArrange menunjukkan bahwa perubahan pada properti ini memerlukan perubahan pada penyajian UI yang biasanya tidak memerlukan perubahan dalam ruang khusus, tetapi menunjukkan bahwa posisi dalam ruang telah berubah. Misalnya, properti "Perataan" harus memiliki set bendera ini.

    • AffectsRender menunjukkan bahwa beberapa perubahan lain telah terjadi yang tidak akan memengaruhi tata letak dan pengukuran, tetapi memerlukan render lain. Contohnya adalah properti yang mengubah warna elemen yang ada, seperti "Latar Belakang".

    • Bendera ini sering digunakan sebagai protokol dalam metadata untuk implementasi penggantian sistem properti atau panggilan balik tata letak Anda sendiri. Misalnya, Anda mungkin memiliki OnPropertyChanged panggilan balik yang akan memanggil InvalidateArrange jika ada properti instans yang melaporkan perubahan nilai dan memiliki AffectsArrange seperti true dalam metadatanya.

  • Beberapa properti dapat memengaruhi karakteristik penyajian elemen induk yang berisi, dengan cara di atas dan di luar perubahan ukuran yang diperlukan yang disebutkan di atas. Contohnya adalah properti yang MinOrphanLines digunakan dalam model dokumen alur, di mana perubahan pada properti tersebut dapat mengubah penyajian keseluruhan dokumen alur yang berisi paragraf. Gunakan AffectsParentArrange atau AffectsParentMeasure untuk mengidentifikasi kasus serupa di properti Anda sendiri.

  • Secara default, properti dependensi mendukung pengikatan data. Anda dapat dengan sengaja menonaktifkan pengikatan data, untuk kasus di mana tidak ada skenario realistis untuk pengikatan data, atau di mana performa dalam pengikatan data untuk objek besar dikenali sebagai masalah.

  • Secara default, pengikatan Mode data untuk properti dependensi default ke OneWay. Anda selalu dapat mengubah pengikatan menjadi TwoWay per instans pengikatan; untuk detailnya, lihat Menentukan Arah Pengikatan. Tetapi sebagai pembuat properti dependensi, Anda dapat memilih untuk membuat properti menggunakan TwoWay mode pengikatan secara default. Contoh properti dependensi yang ada adalah MenuItem.IsSubmenuOpen; skenario untuk properti ini adalah bahwa IsSubmenuOpen logika pengaturan dan MenuItem pembuatan interaksi dengan gaya tema default. Logika IsSubmenuOpen properti menggunakan pengikatan data secara asli untuk mempertahankan status properti sesuai dengan properti status dan panggilan metode lainnya. Contoh properti lain yang mengikat TwoWay secara default adalah TextBox.Text.

  • Anda juga dapat mengaktifkan pewarisan properti di properti dependensi kustom dengan mengatur Inherits bendera. Pewarisan properti berguna untuk skenario di mana elemen induk dan elemen turunan memiliki properti yang sama, dan masuk akal bagi elemen anak agar nilai properti tertentu diatur ke nilai yang sama dengan induk yang mengaturnya. Contoh properti yang dapat diwariskan adalah DataContext, yang digunakan untuk mengikat operasi untuk mengaktifkan skenario detail master penting untuk presentasi data. Dengan membuat DataContext dapat diwariskan, elemen turunan apa pun mewarisi konteks data tersebut juga. Karena pewarisan nilai properti, Anda dapat menentukan konteks data di halaman atau akar aplikasi, dan tidak perlu menentukan ulang untuk pengikatan di semua elemen anak yang mungkin. DataContext juga merupakan contoh yang baik untuk menggambarkan bahwa warisan mengambil alih nilai default, tetapi selalu dapat diatur secara lokal pada elemen anak tertentu; untuk detailnya, lihat Menggunakan Pola Detail Master dengan Data Hierarkis. Warisan nilai properti memang memiliki kemungkinan biaya performa, dan dengan demikian harus digunakan dengan hemat; untuk detailnya, lihat Pewarisan Nilai Properti.

  • Atur Journal bendera untuk menunjukkan apakah properti dependensi Anda harus terdeteksi atau digunakan oleh layanan jurnal navigasi. Contohnya SelectedIndex adalah properti; item apa pun yang dipilih dalam kontrol pilihan harus dipertahankan ketika riwayat jurnal dinavigasi.

Properti Dependensi Baca-Saja

Anda dapat menentukan properti dependensi yang bersifat baca-saja. Namun, skenario mengapa Anda mungkin mendefinisikan properti Anda karena baca-saja agak berbeda, seperti prosedur untuk mendaftarkannya dengan sistem properti dan mengekspos pengidentifikasi. Untuk informasi selengkapnya, lihat Properti Dependensi Baca-Saja.

Properti Dependensi Collection-Type

Properti dependensi jenis koleksi memiliki beberapa masalah implementasi tambahan yang perlu dipertimbangkan. Untuk detailnya, lihat Properti Dependensi Jenis Koleksi.

Pertimbangan Keamanan Properti Dependensi

Properti dependensi harus dideklarasikan sebagai properti publik. Bidang pengidentifikasi properti dependensi harus dinyatakan sebagai bidang statis publik. Bahkan jika Anda mencoba mendeklarasikan tingkat akses lain (seperti dilindungi), properti dependensi selalu dapat diakses melalui pengidentifikasi dalam kombinasi dengan API sistem properti. Bahkan bidang pengidentifikasi yang dilindungi berpotensi dapat diakses karena pelaporan metadata atau API penentuan nilai yang merupakan bagian dari sistem properti, seperti LocalValueEnumerator. Untuk informasi selengkapnya, lihat Keamanan Properti Dependensi.

Properti Dependensi dan Konstruktor Kelas

Ada prinsip umum dalam pemrograman kode terkelola (sering diberlakukan oleh alat analisis kode seperti FxCop) bahwa konstruktor kelas tidak boleh memanggil metode virtual. Ini karena konstruktor dapat disebut sebagai inisialisasi dasar konstruktor kelas turunan, dan memasukkan metode virtual melalui konstruktor mungkin terjadi pada status inisialisasi yang tidak lengkap dari instans objek yang sedang dibangun. Ketika Anda berasal dari kelas apa pun yang sudah berasal dari DependencyObject, Anda harus menyadari bahwa 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 yang sangat spesifik. Untuk detailnya, lihat Brankas Pola Konstruktor untuk DependencyObjects.

Baca juga