Share via


Gambaran umum penulisan kontrol

Ekstensibilitas model kontrol Windows Presentation Foundation (WPF) sangat mengurangi kebutuhan untuk membuat kontrol baru. Namun, dalam kasus tertentu Anda mungkin masih perlu membuat kontrol kustom. Topik ini membahas fitur yang meminimalkan kebutuhan Anda untuk membuat kontrol kustom dan model penulisan kontrol yang berbeda di Windows Presentation Foundation (WPF). Topik ini juga menunjukkan cara membuat kontrol baru.

Alternatif untuk Menulis Kontrol Baru

Secara historis, jika Anda ingin mendapatkan pengalaman yang disesuaikan dari kontrol yang ada, Anda terbatas pada mengubah properti standar kontrol, seperti warna latar belakang, lebar batas, dan ukuran font. Jika Anda ingin memperluas tampilan atau perilaku kontrol di luar parameter yang telah ditentukan sebelumnya ini, Anda harus membuat kontrol baru, biasanya dengan mewarisi dari kontrol yang ada dan mengambil alih metode yang bertanggung jawab untuk menggambar kontrol. Meskipun itu masih merupakan opsi, WPF memungkinkan Anda menyesuaikan kontrol yang ada dengan menggunakan con mode tenda l, gaya, templat, dan pemicunya yang kaya. Daftar berikut memberikan contoh bagaimana fitur-fitur ini dapat digunakan untuk membuat pengalaman kustom dan konsisten tanpa harus membuat kontrol baru.

  • Konten Kaya. Banyak kontrol WPF standar mendukung konten yang kaya. Misalnya, properti konten berjenis ButtonObject, sehingga secara teoritis apa pun dapat ditampilkan pada Button. Agar tombol menampilkan gambar dan teks, Anda dapat menambahkan gambar dan ke TextBlock dan StackPanel menetapkan StackPanel ke Content properti . Karena kontrol dapat menampilkan elemen visual WPF dan data sewenang-wenang, tidak perlu membuat kontrol baru atau untuk memodifikasi kontrol yang ada untuk mendukung visualisasi yang kompleks. Untuk informasi selengkapnya tentang con mode tenda l untuk Button dan con mode tenda ls lainnya di WPF, lihat WPF Content Model.

  • Gaya. Style adalah kumpulan nilai yang mewakili properti untuk kontrol. Dengan menggunakan gaya, Anda dapat membuat representasi yang dapat digunakan kembali dari tampilan dan perilaku kontrol yang diinginkan tanpa menulis kontrol baru. Misalnya, asumsikan bahwa Anda ingin semua kontrol Anda TextBlock memiliki font Arial merah dengan ukuran font 14. Anda dapat membuat gaya sebagai sumber daya dan mengatur properti yang sesuai. Kemudian setiap TextBlock yang Anda tambahkan ke aplikasi Anda akan memiliki penampilan yang sama.

  • Templat Data. Memungkinkan DataTemplate Anda menyesuaikan bagaimana data ditampilkan pada kontrol. Misalnya, DataTemplate dapat digunakan untuk menentukan bagaimana data ditampilkan dalam ListBox. Untuk contoh ini, lihat Gambaran Umum Templat Data. Selain menyesuaikan tampilan data, DataTemplate dapat menyertakan elemen UI, yang memberi Anda banyak fleksibilitas dalam UI kustom. Misalnya, dengan menggunakan DataTemplate, Anda dapat membuat ComboBox di mana setiap item berisi kotak centang.

  • Templat Kontrol. Banyak kontrol dalam WPF menggunakan ControlTemplate untuk menentukan struktur dan tampilan kontrol, yang memisahkan tampilan kontrol dari fungsionalitas kontrol. Anda dapat mengubah tampilan kontrol secara drastis dengan mendefinisikan ControlTemplateulang . Misalnya, Anda menginginkan kontrol yang terlihat seperti lampu stop. Kontrol ini memiliki antarmuka dan fungsionalitas pengguna yang sederhana. Kontrolnya adalah tiga lingkaran, hanya satu yang dapat dinyalakan pada satu waktu. Setelah beberapa pantulan, Anda mungkin menyadari bahwa menawarkan RadioButton fungsionalitas hanya satu yang dipilih pada satu waktu, tetapi tampilan default terlihat RadioButton tidak seperti lampu pada lampu stop. RadioButton Karena menggunakan templat kontrol untuk menentukan tampilannya, mudah untuk menentukan ControlTemplate ulang agar sesuai dengan persyaratan kontrol, dan menggunakan tombol radio untuk membuat lampu henti Anda.

    Catatan

    Meskipun dapat RadioButton menggunakan DataTemplate, a DataTemplate tidak cukup dalam contoh ini. DataTemplate menentukan tampilan konten kontrol. Dalam kasus RadioButton, konten adalah apa pun yang muncul di sebelah kanan lingkaran yang menunjukkan apakah RadioButton dipilih. Dalam contoh lampu stop, tombol radio hanya perlu lingkaran yang dapat "menyala." Karena persyaratan penampilan untuk lampu stop sangat berbeda dari RadioButtontampilan default , perlu untuk menentukan ControlTemplateulang . Secara umum DataTemplate digunakan untuk menentukan konten (atau data) kontrol, dan ControlTemplate digunakan untuk menentukan bagaimana kontrol disusun.

  • Pemicu. A Trigger memungkinkan Anda mengubah tampilan dan perilaku kontrol secara dinamis tanpa membuat kontrol baru. Misalnya, Anda memiliki beberapa ListBox kontrol dalam aplikasi Anda dan ingin item di masing-masing ListBox menjadi tebal dan merah saat dipilih. Naluri pertama Anda mungkin membuat kelas yang mewarisi ListBox dan mengambil alih OnSelectionChanged metode untuk mengubah tampilan item yang dipilih, tetapi pendekatan yang lebih baik adalah menambahkan pemicu ke gaya ListBoxItem yang mengubah tampilan item yang dipilih. Pemicu memungkinkan Anda mengubah nilai properti atau mengambil tindakan berdasarkan nilai properti. Memungkinkan EventTrigger Anda mengambil tindakan saat peristiwa terjadi.

Untuk informasi selengkapnya tentang gaya, templat, dan pemicu, lihat Gaya dan Templat.

Secara umum, jika kontrol Anda mencerminkan fungsionalitas kontrol yang ada, tetapi Anda ingin kontrol terlihat berbeda, Anda harus terlebih dahulu mempertimbangkan apakah Anda dapat menggunakan salah satu metode yang dibahas di bagian ini untuk mengubah tampilan kontrol yang ada.

Model untuk Penulisan Kontrol

Con mode tenda l, gaya, templat, dan pemicu yang kaya meminimalkan kebutuhan Anda untuk membuat kontrol baru. Namun, jika Anda perlu membuat kontrol baru, penting untuk memahami model penulisan kontrol yang berbeda di WPF. WPF menyediakan tiga model umum untuk membuat kontrol, yang masing-masing menyediakan serangkaian fitur dan tingkat fleksibilitas yang berbeda. Kelas dasar untuk ketiga model tersebut adalah UserControl, , Controldan FrameworkElement.

Berasal dari UserControl

Cara paling sederhana untuk membuat kontrol di WPF adalah dengan berasal dari UserControl. Saat Anda membangun kontrol yang mewarisi dari UserControl, Anda menambahkan komponen yang ada ke UserControl, beri nama komponen, dan referensi penanganan aktivitas di XAML. Anda kemudian dapat mereferensikan elemen bernama dan menentukan penanganan aktivitas dalam kode. Model pengembangan ini sangat mirip dengan model yang digunakan untuk pengembangan aplikasi di WPF.

Jika dibangun dengan benar, UserControl dapat memanfaatkan manfaat konten, gaya, dan pemicu yang kaya. Namun, jika kontrol Anda mewarisi dari UserControl, orang yang menggunakan kontrol Anda tidak akan dapat menggunakan DataTemplate atau ControlTemplate untuk menyesuaikan tampilannya. Perlu untuk berasal dari Control kelas atau salah satu kelas turunannya (selain UserControl) untuk membuat kontrol kustom yang mendukung templat.

Manfaat Mendapatkan dari UserControl

Pertimbangkan untuk berasal dari UserControl jika semua hal berikut ini berlaku:

  • Anda ingin membangun kontrol Anda mirip dengan cara Anda membangun aplikasi.

  • Kontrol Anda hanya terdiri dari komponen yang ada.

  • Anda tidak perlu mendukung kustomisasi yang kompleks.

Berasal dari Kontrol

Berasal dari Control kelas adalah model yang digunakan oleh sebagian besar kontrol WPF yang ada. Saat Anda membuat kontrol yang mewarisi dari Control kelas , Anda menentukan tampilannya dengan menggunakan templat. Dengan demikian, Anda memisahkan logika operasional dari representasi visual. Anda juga dapat memastikan pemisahan UI dan logika dengan menggunakan perintah dan pengikatan alih-alih peristiwa dan menghindari elemen referensi jika ControlTemplate memungkinkan. Jika UI dan logika kontrol Anda dipisahkan dengan benar, pengguna kontrol Anda dapat menentukan ulang kontrol ControlTemplate untuk menyesuaikan tampilannya. Meskipun membangun kustom Control tidak sesingkat membangun UserControl, kustom Control memberikan fleksibilitas terbanyak.

Manfaat Turunan dari Kontrol

Pertimbangkan untuk berasal dari Control alih-alih menggunakan UserControl kelas jika salah satu hal berikut berlaku:

  • Anda ingin tampilan kontrol Anda dapat disesuaikan melalui ControlTemplate.

  • Anda ingin kontrol Anda mendukung tema yang berbeda.

Berasal dari FrameworkElement

Kontrol yang berasal dari UserControl atau Control diandalkan setelah menyusun elemen yang ada. Untuk banyak skenario, ini adalah solusi yang dapat diterima, karena objek apa pun yang mewarisi FrameworkElement dapat berada di ControlTemplate. Namun, ada kalanya tampilan kontrol membutuhkan lebih dari fungsionalitas komposisi elemen sederhana. Untuk skenario ini, mendinginkan komponen adalah FrameworkElement pilihan yang tepat.

Ada dua metode standar untuk membangun FrameworkElementkomponen berbasis: penyajian langsung dan komposisi elemen kustom. Penyajian langsung melibatkan pengesampingan OnRenderFrameworkElementDrawingContext metode dan menyediakan operasi yang secara eksplisit menentukan visual komponen. Ini adalah metode yang digunakan oleh Image dan Border. Komposisi elemen kustom melibatkan penggunaan objek jenis Visual untuk menyusun tampilan komponen Anda. Misalnya, lihat Menggunakan Objek DrawingVisual. Track adalah contoh kontrol dalam WPF yang menggunakan komposisi elemen kustom. Dimungkinkan juga untuk mencampur penyajian langsung dan komposisi elemen kustom dalam kontrol yang sama.

Manfaat Mendapatkan dari FrameworkElement

Pertimbangkan untuk berasal dari FrameworkElement jika salah satu hal berikut berlaku:

  • Anda ingin memiliki kontrol yang tepat atas penampilan kontrol Anda di luar apa yang disediakan oleh komposisi elemen sederhana.

  • Anda ingin menentukan tampilan kontrol Anda dengan menentukan logika render Anda sendiri.

  • Anda ingin menyusun elemen yang ada dengan cara baru yang melampaui apa yang mungkin dengan UserControl dan Control.

Mengontrol Dasar-Dasar Penulisan

Seperti yang dibahas sebelumnya, salah satu fitur WPF yang paling kuat adalah kemampuan untuk melampaui pengaturan properti dasar kontrol untuk mengubah penampilan dan perilakunya, namun masih tidak perlu membuat kontrol kustom. Fitur gaya, pengikatan data, dan pemicu dimungkinkan oleh sistem properti WPF dan sistem peristiwa WPF. Bagian berikut menjelaskan beberapa praktik yang harus Anda ikuti, terlepas dari model yang Anda gunakan untuk membuat kontrol kustom, sehingga pengguna kontrol kustom Anda dapat menggunakan fitur-fitur ini seperti yang mereka lakukan untuk kontrol yang disertakan dengan WPF.

Gunakan Properti Dependensi

Ketika properti adalah properti dependensi, dimungkinkan untuk melakukan hal berikut:

  • Atur properti dalam gaya.

  • Ikat properti ke sumber data.

  • Gunakan sumber daya dinamis sebagai nilai properti.

  • Animasikan properti.

Jika Anda ingin properti kontrol Anda mendukung salah satu fungsionalitas ini, Anda harus menerapkannya sebagai properti dependensi. Contoh berikut mendefinisikan properti dependensi bernama Value dengan melakukan hal berikut:

  • DependencyProperty Tentukan pengidentifikasi bernama ValueProperty sebagai publicstaticreadonly bidang.

  • Daftarkan nama properti dengan sistem properti, dengan memanggil DependencyProperty.Register, untuk menentukan hal berikut:

  • Tentukan properti pembungkus CLR bernama Value, yang merupakan nama yang sama yang digunakan untuk mendaftarkan properti dependensi, dengan mengimplementasikan properti get dan set aksesor. Perhatikan bahwa pengaktif get dan set hanya memanggil GetValue dan SetValue masing-masing. Disarankan agar aksesor properti dependensi tidak berisi logika tambahan karena klien dan WPF dapat melewati aksesor dan memanggil GetValue dan SetValue secara langsung. Misalnya, ketika properti terikat ke sumber data, aksesor properti set tidak dipanggil. Alih-alih menambahkan logika tambahan ke aksesor dapatkan dan atur, gunakan ValidateValueCallback, , CoerceValueCallbackdan PropertyChangedCallback delegasi untuk merespons atau memeriksa nilai saat berubah. Untuk informasi selengkapnya tentang panggilan balik ini, lihat Panggilan Balik dan Validasi Properti Dependensi.

  • Tentukan metode untuk yang CoerceValueCallback bernama CoerceValue. CoerceValue memastikan bahwa Value lebih besar atau sama dengan MinValue dan kurang dari atau sama dengan MaxValue.

  • Tentukan metode untuk PropertyChangedCallback, bernama OnValueChanged. OnValueChangedRoutedPropertyChangedEventArgs<T> membuat objek dan bersiap untuk menaikkan peristiwa yang dirutekanValueChanged. Peristiwa yang dirutekan dibahas di bagian berikutnya.

/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(
        "Value", typeof(decimal), typeof(NumericUpDown),
        new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
                                      new CoerceValueCallback(CoerceValue)));

/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{
    get { return (decimal)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static object CoerceValue(DependencyObject element, object value)
{
    decimal newValue = (decimal)value;
    NumericUpDown control = (NumericUpDown)element;

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));

    return newValue;
}

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    NumericUpDown control = (NumericUpDown)obj;			

    RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
        (decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
    control.OnValueChanged(e);
}
''' <summary>
''' Identifies the Value dependency property.
''' </summary>
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))

''' <summary>
''' Gets or sets the value assigned to the control.
''' </summary>
Public Property Value() As Decimal
    Get
        Return CDec(GetValue(ValueProperty))
    End Get
    Set(ByVal value As Decimal)
        SetValue(ValueProperty, value)
    End Set
End Property

Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
    Dim newValue As Decimal = CDec(value)
    Dim control As NumericUpDown = CType(element, NumericUpDown)

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))

    Return newValue
End Function

Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
    Dim control As NumericUpDown = CType(obj, NumericUpDown)

    Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
    control.OnValueChanged(e)
End Sub

Untuk informasi selengkapnya, lihat Properti Dependensi Kustom.

Menggunakan Peristiwa Yang Dirutekan

Sama seperti properti dependensi memperluas gagasan properti CLR dengan fungsionalitas tambahan, peristiwa yang dirutekan memperluas gagasan peristiwa CLR standar. Saat Anda membuat kontrol WPF baru, ada baiknya juga untuk mengimplementasikan peristiwa Anda sebagai peristiwa yang dirutekan karena peristiwa yang dirutekan mendukung perilaku berikut:

  • Peristiwa dapat ditangani pada induk dari beberapa kontrol. Jika peristiwa adalah peristiwa yang menggelegak, induk tunggal di pohon elemen dapat berlangganan peristiwa tersebut. Kemudian penulis aplikasi dapat menggunakan satu handler untuk merespons peristiwa beberapa kontrol. Misalnya, jika kontrol Anda adalah bagian dari setiap item dalam ListBox (karena disertakan dalam DataTemplate), pengembang aplikasi dapat menentukan penanganan aktivitas untuk peristiwa kontrol Anda pada ListBox. Setiap kali peristiwa terjadi pada salah satu kontrol, penanganan aktivitas dipanggil.

  • Peristiwa yang dirutekan dapat digunakan dalam EventSetter, yang memungkinkan pengembang aplikasi menentukan handler peristiwa dalam gaya.

  • Peristiwa yang dirutekan dapat digunakan dalam EventTrigger, yang berguna untuk menganimasikan properti dengan menggunakan XAML. Untuk informasi selengkapnya, lihat gambaran umum Animation.

Contoh berikut mendefinisikan peristiwa yang dirutekan dengan melakukan hal berikut:

  • RoutedEvent Tentukan pengidentifikasi bernama ValueChangedEvent sebagai publicstaticreadonly bidang.

  • Daftarkan peristiwa yang dirutekan dengan memanggil EventManager.RegisterRoutedEvent metode . Contoh menentukan informasi berikut saat memanggil RegisterRoutedEvent:

    • Nama peristiwanya adalah ValueChanged.

    • Strategi perutean adalah Bubble, yang berarti bahwa penanganan aktivitas pada sumber (objek yang menaikkan peristiwa) dipanggil terlebih dahulu, dan kemudian penanganan aktivitas pada elemen induk sumber dipanggil berturut-turut, dimulai dengan penanganan aktivitas pada elemen induk terdekat.

    • Jenis penanganan aktivitas adalah RoutedPropertyChangedEventHandler<T>, dibangun dengan Decimal jenis .

    • Jenis pemilik peristiwa adalah NumericUpDown.

  • Deklarasikan acara publik bernama ValueChanged dan menyertakan deklarasi aksesor peristiwa. Contoh panggilan AddHandler dalam add deklarasi aksesor dan RemoveHandler dalam remove deklarasi aksesor untuk menggunakan layanan peristiwa WPF.

  • Buat metode virtual yang dilindungi bernama OnValueChanged yang memunculkan ValueChanged peristiwa.

/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
    "ValueChanged", RoutingStrategy.Bubble,
    typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
    add { AddHandler(ValueChangedEvent, value); }
    remove { RemoveHandler(ValueChangedEvent, value); }
}

/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
    RaiseEvent(args);
}
''' <summary>
''' Identifies the ValueChanged routed event.
''' </summary>
Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))

''' <summary>
''' Occurs when the Value property changes.
''' </summary>
Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
    AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.AddHandler(ValueChangedEvent, value)
    End AddHandler
    RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.RemoveHandler(ValueChangedEvent, value)
    End RemoveHandler
    RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
    End RaiseEvent
End Event

''' <summary>
''' Raises the ValueChanged event.
''' </summary>
''' <param name="args">Arguments associated with the ValueChanged event.</param>
Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
    MyBase.RaiseEvent(args)
End Sub

Untuk informasi selengkapnya, lihat Gambaran Umum Peristiwa Yang Dirutekan dan Membuat Peristiwa Rute Kustom.

Gunakan Pengikatan

Untuk memisahkan UI kontrol Anda dari logikanya, pertimbangkan untuk menggunakan pengikatan data. Ini sangat penting jika Anda menentukan tampilan kontrol Anda dengan menggunakan ControlTemplate. Saat Anda menggunakan pengikatan data, Anda mungkin dapat menghilangkan kebutuhan untuk mereferensikan bagian tertentu dari UI dari kode. Ada baiknya untuk menghindari elemen referensi yang ada di ControlTemplate karena ketika kode mereferensikan elemen yang ada di ControlTemplate dan ControlTemplate diubah, elemen yang dirujuk perlu disertakan dalam yang baru ControlTemplate.

Contoh berikut memperbarui TextBlockNumericUpDown kontrol, menetapkan nama dan mereferensikan kotak teks berdasarkan nama dalam kode.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
  <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
private void UpdateTextBlock()
{
    valueText.Text = Value.ToString();
}
Private Sub UpdateTextBlock()
    valueText.Text = Value.ToString()
End Sub

Contoh berikut menggunakan pengikatan untuk mencapai hal yang sama.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

    <!--Bind the TextBlock to the Value property-->
    <TextBlock 
        Width="60" TextAlignment="Right" Padding="5"
        Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type local:NumericUpDown}}, 
                       Path=Value}"/>

</Border>

Untuk informasi selengkapnya tentang pengikatan data, lihat Gambaran Umum Pengikatan Data.

Desain untuk Desainer

Untuk menerima dukungan untuk kontrol WPF kustom di WPF Designer untuk Visual Studio (misalnya, pengeditan properti dengan jendela Properti), ikuti panduan ini. Untuk informasi selengkapnya tentang pengembangan untuk Perancang WPF, lihat Mendesain XAML di Visual Studio.

Properti Dependensi

Pastikan untuk mengimplementasikan CLR get dan set aksesor seperti yang dijelaskan sebelumnya, di "Gunakan Properti Dependensi." Desainer dapat menggunakan pembungkus untuk mendeteksi keberadaan properti dependensi, tetapi mereka, seperti WPF dan klien kontrol, tidak diperlukan untuk memanggil aksesor saat mendapatkan atau mengatur properti.

Properti yang Terlampir

Anda harus menerapkan properti terlampir pada kontrol kustom menggunakan panduan berikut:

  • Memiliki publicreadonlyDependencyPropertystaticformulir PropertyNameProperty yang dibuat menggunakan RegisterAttached metode . Nama properti yang diteruskan harus cocok dengan RegisterAttached PropertyName.

  • Terapkan sepasang metode CLR bernama SetPropertyName dan GetPropertyName.publicstatic Kedua metode harus menerima kelas yang berasal dari DependencyProperty sebagai argumen pertama mereka. Metode SetPropertyName juga menerima argumen yang jenisnya cocok dengan jenis data terdaftar untuk properti . Metode GetPropertyName harus mengembalikan nilai dengan jenis yang sama. SetJika metode PropertyName hilang, properti ditandai baca-saja.

  • SetPropertyName dan GetPropertyName harus merutekan langsung ke GetValue metode dan SetValue pada objek dependensi target. Desainer dapat mengakses properti terlampir dengan memanggil melalui pembungkus metode atau melakukan panggilan langsung ke objek dependensi target.

Untuk informasi selengkapnya tentang properti terlampir, lihat Gambaran Umum Properti Terlampir.

Tentukan dan Gunakan Sumber Daya Bersama

Anda dapat menyertakan kontrol dalam rakitan yang sama dengan aplikasi Anda, atau Anda dapat mengemas kontrol Anda dalam rakitan terpisah yang dapat digunakan dalam beberapa aplikasi. Sebagian besar, informasi yang dibahas dalam topik ini berlaku terlepas dari metode yang Anda gunakan. Namun, ada satu perbedaan yang perlu dicatat. Ketika Anda menempatkan kontrol di rakitan yang sama dengan aplikasi, Anda bebas menambahkan sumber daya global ke file App.xaml. Tetapi rakitan yang hanya berisi kontrol tidak memiliki objek yang Application terkait dengannya, sehingga file App.xaml tidak tersedia.

Saat aplikasi mencari sumber daya, aplikasi melihat tiga tingkat dalam urutan berikut:

  1. Tingkat elemen.

    Sistem dimulai dengan elemen yang mereferensikan sumber daya dan kemudian mencari sumber daya induk logis dan sebagainya sampai elemen akar tercapai.

  2. Tingkat aplikasi.

    Sumber daya yang ditentukan oleh Application objek.

  3. Tingkat tema.

    Kamus tingkat tema disimpan dalam subfolder bernama Tema. File dalam folder Tema sesuai dengan tema. Misalnya, Anda mungkin memiliki Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, dan sebagainya. Anda juga dapat memiliki file bernama generic.xaml. Ketika sistem mencari sumber daya di tingkat tema, sistem pertama-tama mencarinya dalam file khusus tema dan kemudian mencarinya di generic.xaml.

Ketika kontrol Anda berada dalam rakitan yang terpisah dari aplikasi, Anda harus menempatkan sumber daya global Anda pada tingkat elemen atau pada tingkat tema. Kedua metode memiliki kelebihannya.

Menentukan Sumber Daya di Tingkat Elemen

Anda dapat menentukan sumber daya bersama di tingkat elemen dengan membuat kamus sumber daya kustom dan menggabungkannya dengan kamus sumber daya kontrol Anda. Saat Anda menggunakan metode ini, Anda dapat memberi nama file sumber daya apa pun yang Anda inginkan, dan dapat berada di folder yang sama dengan kontrol Anda. Sumber daya di tingkat elemen juga dapat menggunakan string sederhana sebagai kunci. Contoh berikut membuat LinearGradientBrush file sumber daya bernama Dictionary1.xaml.

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <LinearGradientBrush 
    x:Key="myBrush"  
    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />
  </LinearGradientBrush>
  
</ResourceDictionary>

Setelah menentukan kamus, Anda perlu menggabungkannya dengan kamus sumber daya kontrol Anda. Anda dapat melakukan ini dengan menggunakan XAML atau kode.

Contoh berikut menggabungkan kamus sumber daya dengan menggunakan XAML.

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

Kerugian dari pendekatan ini adalah bahwa ResourceDictionary objek dibuat setiap kali Anda mereferensikannya. Misalnya, jika Anda memiliki 10 kontrol kustom di pustaka Anda dan menggabungkan kamus sumber daya bersama untuk setiap kontrol dengan menggunakan XAML, Anda membuat 10 objek yang identik ResourceDictionary . Anda dapat menghindari hal ini dengan membuat kelas statis yang menggabungkan sumber daya dalam kode dan mengembalikan hasil ResourceDictionary.

Contoh berikut membuat kelas yang mengembalikan bersama ResourceDictionary.

internal static class SharedDictionaryManager
{
    internal static ResourceDictionary SharedDictionary
    {
        get
        {
            if (_sharedDictionary == null)
            {
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml",
                                    System.UriKind.Relative);

                _sharedDictionary =
                    (ResourceDictionary)Application.LoadComponent(resourceLocater);
            }

            return _sharedDictionary;
        }
    }

    private static ResourceDictionary _sharedDictionary;
}

Contoh berikut menggabungkan sumber daya bersama dengan sumber daya kontrol kustom di konstruktor kontrol sebelum memanggil InitializeComponent. SharedDictionaryManager.SharedDictionary Karena adalah properti statis, ResourceDictionary properti dibuat hanya sekali. Karena kamus sumber daya digabungkan sebelum InitializeComponent dipanggil, sumber daya tersedia untuk kontrol dalam file XAML-nya.

public NumericUpDown()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();
}

Menentukan Sumber Daya di Tingkat Tema

WPF memungkinkan Anda membuat sumber daya untuk tema Windows yang berbeda. Sebagai penulis kontrol, Anda dapat menentukan sumber daya untuk tema tertentu untuk mengubah tampilan kontrol Anda tergantung pada tema apa yang digunakan. Misalnya, tampilan Button dalam tema Windows Classic (tema default untuk Windows 2000) berbeda dari Button dalam tema Windows Luna (tema default untuk Windows XP) karena Button menggunakan yang berbeda ControlTemplate untuk setiap tema.

Sumber daya yang khusus untuk tema disimpan dalam kamus sumber daya dengan nama file tertentu. File-file ini harus berada dalam folder bernama Themes yang merupakan subfolder folder yang berisi kontrol. Tabel berikut mencantumkan file kamus sumber daya dan tema yang terkait dengan setiap file:

Nama file kamus sumber daya Tema Windows
Classic.xaml Tampilan Windows 9x/2000 klasik di Windows XP
Luna.NormalColor.xaml Tema biru default pada Windows XP
Luna.Homestead.xaml Tema zaitun di Windows XP
Luna.Metallic.xaml Tema silver di Windows XP
Royale.NormalColor.xaml Tema default pada Windows XP Media Center Edition
Aero.NormalColor.xaml Tema default di Windows Vista

Anda tidak perlu menentukan sumber daya untuk setiap tema. Jika sumber daya tidak didefinisikan untuk tema tertentu, kontrol akan memeriksa Classic.xaml sumber daya. Jika sumber daya tidak ditentukan dalam file yang sesuai dengan tema saat ini atau di Classic.xaml, kontrol menggunakan sumber daya generik, yang berada dalam file kamus sumber daya bernama generic.xaml. File generic.xaml terletak di folder yang sama dengan file kamus sumber daya khusus tema. Meskipun generic.xaml tidak sesuai dengan tema Windows tertentu, ini masih merupakan kamus tingkat tema.

Kontrol kustom C# atau Visual Basic NumericUpDown dengan tema dan sampel dukungan otomatisasi UI berisi dua kamus sumber daya untuk NumericUpDown kontrol: satu berada di generic.xaml, dan yang lainnya ada di Luna.NormalColor.xaml.

Saat Anda memasukkan ControlTemplate salah satu file kamus sumber daya khusus tema, Anda harus membuat konstruktor statis untuk kontrol Anda dan memanggil OverrideMetadata(Type, PropertyMetadata) metode pada DefaultStyleKey, seperti yang ditunjukkan dalam contoh berikut.

static NumericUpDown()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Menentukan dan Mereferensikan Kunci untuk Sumber Daya Tema

Saat menentukan sumber daya di tingkat elemen, Anda dapat menetapkan string sebagai kuncinya dan mengakses sumber daya melalui string. Saat Anda menentukan sumber daya di tingkat tema, Anda harus menggunakan ComponentResourceKey sebagai kunci. Contoh berikut mendefinisikan sumber daya di generic.xaml.

<LinearGradientBrush 
     x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter}, 
                                  ResourceId=MyEllipseBrush}"  
                                  StartPoint="0,0" EndPoint="1,0">
    <GradientStop Color="Blue" Offset="0" />
    <GradientStop Color="Red" Offset="0.5" />
    <GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>

Contoh berikut mereferensikan sumber daya dengan menentukan ComponentResourceKey sebagai kunci.

<RepeatButton 
    Grid.Column="1" Grid.Row="0"
    Background="{StaticResource {ComponentResourceKey 
                        TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                        ResourceId=ButtonBrush}}">
    Up
</RepeatButton>
<RepeatButton 
    Grid.Column="1" Grid.Row="1"
    Background="{StaticResource {ComponentResourceKey 
                    TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                    ResourceId=ButtonBrush}}">
    Down
 </RepeatButton>
Menentukan Lokasi Sumber Daya Tema

Untuk menemukan sumber daya untuk kontrol, aplikasi hosting perlu mengetahui bahwa rakitan berisi sumber daya khusus kontrol. Anda dapat menyelesaikannya dengan menambahkan ke ThemeInfoAttribute rakitan yang berisi kontrol. ThemeInfoAttribute memiliki GenericDictionaryLocation properti yang menentukan lokasi sumber daya generik, dan ThemeDictionaryLocation properti yang menentukan lokasi sumber daya khusus tema.

Contoh berikut mengatur GenericDictionaryLocation properti dan ThemeDictionaryLocation ke SourceAssembly, untuk menentukan bahwa sumber daya generik dan tema khusus berada dalam rakitan yang sama dengan kontrol.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
           ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>

Baca juga