Bagikan melalui


Properti dependensi khusus

Pengembang aplikasi dan penulis komponen Windows Presentation Foundation (WPF) dapat membuat properti dependensi kustom untuk memperluas fungsionalitas properti mereka. Tidak seperti properti pada lingkungan runtime bahasa umum (CLR), properti dependensi menambahkan dukungan untuk penataan, pengikatan, pewarisan, animasi, dan nilai bawaan. 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.

Prasyarat

Artikel ini mengasumsikan pengetahuan dasar tentang properti dependensi, dan 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 pada sistem properti WPF melalui panggilan Register atau RegisterReadOnly. Metode Register mengembalikan instans DependencyProperty yang menyimpan nama terdaftar dan karakteristik properti dependensi. Anda akan menetapkan instans DependencyProperty ke bidang readonly statis, yang dikenal sebagai pengidentifikasi properti dependensi , yang sesuai dengan konvensi diberi nama <property name>Property. Misalnya, bidang pengidentifikasi untuk properti Background selalu BackgroundProperty.

Pengidentifikasi properti ketergantungan digunakan sebagai field pendukung untuk mengambil atau menetapkan nilai properti, bukan pola standar mendukung pengaturan properti dengan field 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 jenis DependencyObject. 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 bukan properti terlampir diekspos oleh pembungkus CLR yang mengimplementasikan akses get dan set. 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 dasar melalui panggilan DependencyObject.GetValue dan DependencyObject.SetValue, mengoper pengidentifikasi properti dependensi sebagai parameter. Konsumen properti dependensi biasanya tidak memanggil 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 pengidentifikasi DependencyProperty. Apakah bermanfaat untuk membuat properti dependensi tergantung pada skenario Anda. Meskipun menunjang properti Anda dengan ruang 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 sebuah gaya. Untuk informasi selengkapnya, lihat Gaya dan templat .

  • 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 lebih lanjut, 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 ke CLR. Untuk informasi selengkapnya, lihat Pewarisan nilai properti.

  • Properti-properti yang dapat dianimasikan. Untuk informasi selengkapnya, 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 properti Dependensi dan validasi.

  • 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.

    • Tetapkan nilai baku properti dependensi dengan mengesampingkan 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 penggantian metadata itu praktis tergantung pada skenario Anda, dan seberapa mirip skenario tersebut dengan implementasi yang ada dari properti dan kelas ketergantungan WPF. Untuk informasi selengkapnya tentang mengesampingkan 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. Tentukan pengenal DependencyProperty sebagai field public static readonly pada jenis pemilik. Nama bidang pengidentifikasi adalah nama properti dengan akhiran Property ditambahkan.

  4. Tentukan properti pembungkus CLR dengan nama yang sama persis dengan nama properti dependensi. Dalam bingkai kerja CLR, terapkan akses get dan set yang terhubung dengan properti dependensi yang mendukung bingkai kerja tersebut.

Mendaftarkan properti

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

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

Nota

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 yang Anda buat harus berupa nama properti terdaftar dengan akhiran Property.

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 dari mana kelas Anda tidak mewarisi, 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 pertama panggilan Register, yang merupakan nama properti dependensi. Implementasi pembungkus Anda akan memanggil GetValue di aksesor get, dan SetValue di aksesor set (untuk properti baca-tulis). Contoh berikut menunjukkan pembungkus—setelah 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 kode GetValue 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 pemuatan XAML dan properti dependensi.

Metadata properti dependensi

Saat Anda mendaftarkan properti dependensi, sistem properti membuat objek metadata untuk menyimpan karakteristik properti. Kelebihan beban metode Register 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 metadata yang lebih khusus FrameworkPropertyMetadata daripada kelas dasarnya PropertyMetadata. Beberapa tanda tangan konstruktor FrameworkPropertyMetadata memungkinkan Anda menentukan kombinasi karakteristik metadata yang berbeda. Jika Anda hanya ingin menentukan nilai default, gunakan FrameworkPropertyMetadata(Object) dan teruskan nilai default ke parameter Object. Pastikan bahwa tipe nilai cocok dengan propertyType yang ditentukan dalam panggilan Register.

Beberapa kelebihan beban FrameworkPropertyMetadata 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 properti Width.

    • 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 properti Alignment.

    • AffectsRender, yang menunjukkan bahwa perubahan telah terjadi yang tidak memengaruhi tata letak dan pengukuran, tetapi masih memerlukan render lain. Misalnya, atur bendera ini untuk properti Background, 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 suatu panggilan balik OnPropertyChanged untuk memanggil InvalidateArrange saat properti dari instans melaporkan adanya perubahan nilai dan memiliki AffectsArrange yang diatur dalam metadata.

  • Beberapa properti memengaruhi karakteristik penyajian elemen induknya dengan cara lain. Misalnya, perubahan pada properti MinOrphanLines 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 saat 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 OneWay, Anda dapat mengubah mode pengikatan tertentu ke TwoWay. Untuk informasi lebih lanjut, lihat Arah pengikatan. Sebagai penulis properti dependensi, Anda bahkan dapat memilih untuk menjadikan mode default pengikatan dua arah. Contoh properti dependensi yang ada yang menggunakan pengikatan data dua arah adalah MenuItem.IsSubmenuOpen, yang memiliki status yang didasarkan pada properti dan panggilan metode lainnya. Skenario untuk IsSubmenuOpen adalah logika pengaturan dan penggabungan MenuItemberinteraksi 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 flag Inherits. 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

  • Atur bendera Journal untuk menunjukkan bahwa properti dependensi Anda harus terdeteksi atau digunakan oleh layanan jurnal navigasi. Misalnya, properti SelectedIndex mengatur bendera Journal untuk merekomendasikan agar aplikasi menyimpan riwayat jurnal 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 ketergantungan hanya-baca.

Properti dependensi jenis koleksi

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 tipe koleksi .

Keamanan properti dependensi

Biasanya, Anda akan mendeklarasikan properti dependensi sebagai properti publik, dan DependencyProperty sebagai bidang pengidentifikasi public static readonly. 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 dari RegisterReadOnlyDependencyPropertyKey, dan biasanya Anda tidak akan membuat DependencyPropertyKey anggota public kelas Anda. Karena sistem properti WPF tidak menyebarluaskan DependencyPropertyKey di luar kode Anda, properti dependensi baca-saja memiliki keamanan set yang lebih baik daripada properti dependensi baca-tulis.

Properti ketergantungan 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 Pola Konstruktor Aman untuk DependencyObjects .

Lihat juga