Bagikan melalui


Gambaran umum pengikatan data (WPF .NET)

Pengikatan data di Windows Presentation Foundation (WPF) menyediakan cara yang sederhana dan konsisten bagi aplikasi untuk menyajikan dan berinteraksi dengan data. Elemen dapat terikat ke data dari berbagai jenis sumber data dalam bentuk objek .NET dan XML. ContentControl apa pun seperti Button dan ItemsControl, seperti ListBox dan ListView, memiliki fungsionalitas bawaan untuk mengaktifkan gaya fleksibel item data tunggal atau pengumpulan item data. Tampilan sortir, filter, dan grup dapat dihasilkan di atas data.

Pengikatan data dalam WPF memiliki beberapa keunggulan dibandingkan model tradisional, termasuk dukungan inheren untuk pengikatan data oleh berbagai properti, representasi antarmuka pengguna data yang fleksibel, dan pemisahan logika bisnis yang bersih dari antarmuka pengguna.

Artikel ini pertama kali membahas konsep dasar untuk pengikatan data WPF dan kemudian mencakup penggunaan Binding kelas dan fitur pengikatan data lainnya.

Penting

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

Apa itu pengikatan data?

Pengikatan data adalah proses yang membuat koneksi antara antarmuka pengguna aplikasi dan logika bisnis. Jika pengikatan memiliki pengaturan yang benar dan data menyediakan pemberitahuan yang tepat, saat data mengubah nilainya, elemen yang terikat pada data mencerminkan perubahan secara otomatis. Pengikatan data juga dapat berarti bahwa jika representasi luar data dalam elemen berubah, maka data yang mendasar dapat diperbarui secara otomatis untuk mencerminkan perubahan. Misalnya, jika pengguna mengedit nilai dalam elemen TextBox, nilai data yang mendasar akan diperbarui secara otomatis untuk mencerminkan perubahan tersebut.

Penggunaan umum pengikatan data adalah menempatkan data konfigurasi server atau lokal ke dalam formulir atau kontrol antarmuka pengguna lainnya. Dalam WPF, konsep ini diperluas untuk menyertakan pengikatan berbagai properti ke berbagai jenis sumber data. Di WPF, properti dependensi elemen dapat terikat ke objek .NET (termasuk objek ADO.NET atau objek yang terkait dengan properti Web Services dan Web) dan data XML.

Konsep pengikatan data dasar

Terlepas dari elemen apa yang Anda ikat dan sifat sumber data Anda, setiap pengikatan selalu mengikuti model yang diilustrasikan oleh gambar berikut.

Diagram that shows the basic data binding model.

Seperti yang ditunjukkan oleh gambar, pengikatan data pada dasarnya adalah jembatan antara target pengikatan Anda dan sumber pengikatan Anda. Gambar menunjukkan konsep pengikatan data WPF dasar berikut:

  • Biasanya, setiap pengikatan memiliki empat komponen:

    • Objek target pengikatan.
    • Properti target.
    • Sumber pengikatan.
    • Jalur ke nilai di sumber pengikatan yang akan digunakan.

    Misalnya, jika Anda mengikat konten TextBox properti Employee.Name, Anda akan menyiapkan pengikatan seperti tabel berikut:

    Pengaturan Nilai
    Target TextBox
    Properti target Text
    Objek sumber Employee
    Jalur nilai objek sumber Name
  • Properti target harus merupakan properti dependensi.

    Sebagian besar properti UIElement adalah properti dependensi, dan sebagian besar properti dependensi, kecuali properti baca-saja, mendukung pengikatan data secara default. Hanya jenis yang berasal dari DependencyObject yang dapat menentukan properti dependensi. Semua jenis UIElement berasal dari DependencyObject.

  • Sumber pengikatan tidak dibatasi untuk objek .NET kustom.

    Meskipun tidak ditampilkan dalam gambar, perlu dicatat bahwa objek sumber pengikatan tidak dibatasi untuk menjadi objek .NET kustom. Pengikatan data WPF mendukung data dalam bentuk objek .NET, XML, dan bahkan objek elemen XAML. Untuk memberikan beberapa contoh, sumber pengikatan Anda mungkin merupakan UIElement, objek daftar apa pun, objek ADO.NET atau Layanan Web, atau XmlNode yang berisi data XML Anda. Untuk informasi selengkapnya, lihat Gambaran umum sumber pengikatan.

Penting untuk diingat bahwa ketika Anda membuat pengikatan, Anda mengikat target pengikatan ke sumber pengikatan. Misalnya, jika Anda menampilkan beberapa data XML pokok dalam ListBox menggunakan pengikatan data, Anda mengikat ListBox ke data XML.

Untuk membuat pengikatan, Anda menggunakan objek Binding. Sisa artikel ini membahas banyak konsep yang terkait dengan dan beberapa properti dan penggunaan objek Binding.

Konteks data

Ketika pengikatan data dideklarasikan pada elemen XAML, mereka menyelesaikan pengikatan data dengan melihat properti DataContext langsung mereka. Konteks data biasanya merupakan objek sumber pengikatan untuk evaluasi jalur nilai sumber pengikatan. Anda dapat mengambil alih perilaku ini dalam pengikatan dan mengatur nilai objek sumber pengikatan tertentu. Jika properti DataContext untuk objek yang menghosting pengikatan tidak diatur, properti elemen DataContext induk diperiksa, dan seterusnya, hingga akar pohon objek XAML. Singkatnya, konteks data yang digunakan untuk mengatasi pengikatan diwariskan dari induk kecuali secara eksplisit diatur pada objek.

Pengikatan dapat dikonfigurasi untuk diselesaikan dengan objek tertentu, dibandingkan dengan menggunakan konteks data untuk resolusi pengikatan. Menentukan objek sumber secara langsung digunakan ketika, misalnya, Anda mengikat warna latar depan objek ke warna latar belakang objek lain. Konteks data tidak diperlukan karena pengikatan diselesaikan di antara kedua objek tersebut. Sebaliknya, pengikatan yang tidak terikat ke objek sumber tertentu menggunakan resolusi konteks data.

Saat properti DataContext berubah, semua pengikatan yang dapat dipengaruhi oleh konteks data dievaluasi kembali.

Arah aliran data

Seperti yang ditunjukkan oleh panah pada gambar sebelumnya, aliran data pengikatan dapat berubah dari target pengikatan ke sumber pengikatan (misalnya, nilai sumber berubah ketika pengguna mengedit nilai TextBox) dan/atau dari sumber pengikatan ke target pengikatan (misalnya, konten TextBox Anda diperbarui dengan perubahan dalam sumber pengikatan) jika sumber pengikatan memberikan pemberitahuan yang tepat.

Anda mungkin ingin aplikasi memungkinkan pengguna mengubah data dan menyebarkannya kembali ke objek sumber. Atau Anda mungkin tidak ingin mengaktifkan pengguna untuk memperbarui data sumber. Anda dapat mengontrol aliran data dengan mengatur Binding.Mode.

Gambar ini menggambarkan berbagai jenis aliran data:

Data binding data flow

  • OneWay pengikatan menyebabkan perubahan pada properti sumber untuk memperbarui properti target secara otomatis, tetapi perubahan pada properti target tidak disebarluaskan kembali ke properti sumber. Jenis pengikatan ini sesuai jika kontrol yang terikat secara implisit baca-saja. Misalnya, Anda dapat mengikat ke sumber seperti ticker stok, atau mungkin properti target Anda tidak memiliki antarmuka kontrol yang disediakan untuk membuat perubahan, seperti warna latar belakang tabel yang terikat data. Jika tidak perlu memantau perubahan properti target, menggunakan mode pengikatan OneWay menghindari overhead mode pengikatan TwoWay.

  • TwoWay pengikatan menyebabkan perubahan pada properti sumber atau properti target untuk memperbarui properti lainnya secara otomatis. Jenis pengikatan ini sesuai untuk bentuk yang dapat diedit atau skenario antarmuka pengguna yang sepenuhnya interaktif lainnya. Sebagian besar properti default ke pengikatan OneWay, tetapi beberapa properti dependensi (biasanya properti kontrol yang dapat diedit pengguna seperti TextBox.Text dan CheckBox.IsChecked default ke pengikatan TwoWay.

    Cara terprogram untuk menentukan apakah properti dependensi mengikat satu arah atau dua arah secara default adalah dengan mendapatkan metadata properti dengan DependencyProperty.GetMetadata. Jenis pengembalian metode ini adalah PropertyMetadata, yang tidak berisi metadata apa pun tentang pengikatan. Namun, jika jenis ini dapat ditransmisikan ke turunan FrameworkPropertyMetadata, maka nilai Boolean properti FrameworkPropertyMetadata.BindsTwoWayByDefault dapat diperiksa. Contoh kode berikut menunjukkan mendapatkan metadata untuk TextBox.Text properti :

    public static void PrintMetadata()
    {
        // Get the metadata for the property
        PropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));
    
        // Check if metadata type is FrameworkPropertyMetadata
        if (metadata is FrameworkPropertyMetadata frameworkMetadata)
        {
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:");
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}");
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}");
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}");
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}");
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}");
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}");
        }
    
        /*  Displays:
         *  
         *  TextBox.Text property metadata:
         *    BindsTwoWayByDefault: True
         *    IsDataBindingAllowed: True
         *          AffectsArrange: False
         *          AffectsMeasure: False
         *           AffectsRender: False
         *                Inherits: False
        */
    }
    
    Public Shared Sub PrintMetadata()
    
        Dim metadata As PropertyMetadata = TextBox.TextProperty.GetMetadata(GetType(TextBox))
        Dim frameworkMetadata As FrameworkPropertyMetadata = TryCast(metadata, FrameworkPropertyMetadata)
    
        If frameworkMetadata IsNot Nothing Then
    
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:")
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}")
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}")
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}")
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}")
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}")
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}")
    
            '  Displays:
            '
            '  TextBox.Text property metadata:
            '    BindsTwoWayByDefault: True
            '    IsDataBindingAllowed: True
            '          AffectsArrange: False
            '          AffectsMeasure: False
            '           AffectsRender: False
            '                Inherits: False
        End If
    
    
    End Sub
    
  • OneWayToSource adalah kebalikan dari pengikatan OneWay; ini memperbarui properti sumber ketika properti target berubah. Salah satu contoh skenario adalah jika Anda hanya perlu mengevaluasi kembali nilai sumber dari antarmuka pengguna.

  • Tidak diilustrasikan dalam gambar adalah pengikatan OneTime, yang menyebabkan properti sumber menginisialisasi properti target tetapi tidak menyebarluaskan perubahan berikutnya. Jika konteks data berubah atau objek dalam konteks data berubah, perubahan tidak tercermin dalam properti target. Jenis pengikatan ini sesuai jika rekam jepret status saat ini sesuai atau datanya benar-benar statis. Jenis pengikatan ini juga berguna jika Anda ingin menginisialisasi properti target Anda dengan beberapa nilai dari properti sumber dan konteks data tidak diketahui sebelumnya. Mode ini pada dasarnya adalah bentuk pengikatan OneWay yang lebih sederhana yang memberikan performa yang lebih baik dalam kasus di mana nilai sumber tidak berubah.

Untuk mendeteksi perubahan sumber (berlaku untuk pengikatan OneWay dan TwoWay), sumber harus menerapkan mekanisme pemberitahuan perubahan properti yang sesuai seperti INotifyPropertyChanged. Lihat Cara: Menerapkan pemberitahuan perubahan properti (.NET Framework) untuk contoh implementasi INotifyPropertyChanged.

Properti Binding.Mode menyediakan informasi selengkapnya tentang mode pengikatan dan contoh cara menentukan arah pengikatan.

Apa yang memicu pembaruan sumber

Pengikatan yang TwoWay atau OneWayToSource mendengarkan perubahan dalam properti target dan menyebarkannya kembali ke sumber, yang dikenal sebagai memperbarui sumber. Misalnya, Anda dapat mengedit teks TextBox untuk mengubah nilai sumber yang mendasar.

Namun, apakah nilai sumber Anda diperbarui saat Mengedit teks atau setelah selesai mengedit teks dan kontrol kehilangan fokus? Properti Binding.UpdateSourceTrigger menentukan apa yang memicu pembaruan sumber. Titik-titik panah kanan dalam gambar berikut mengilustrasikan peran properti Binding.UpdateSourceTrigger.

Diagram that shows the role of the UpdateSourceTrigger property.

Jika nilai UpdateSourceTrigger adalah UpdateSourceTrigger.PropertyChanged, maka nilai yang ditujukan ke panah kanan TwoWay atau pengikatan OneWayToSource diperbarui segera setelah properti target berubah. Namun, jika nilai UpdateSourceTrigger adalah LostFocus, maka nilai tersebut hanya diperbarui dengan nilai baru ketika properti target kehilangan fokus.

Mirip dengan properti Mode, properti dependensi yang berbeda memiliki nilai default UpdateSourceTrigger yang berbeda. Nilai default untuk sebagian besar properti dependensi adalah PropertyChanged, yang menyebabkan nilai properti sumber langsung berubah saat nilai properti target diubah. Perubahan instan baik-baik saja untuk CheckBox dan kontrol sederhana lainnya. Namun, untuk bidang teks, memperbarui setelah setiap penekanan tombol dapat mengurangi performa dan menolak pengguna kesempatan biasa untuk backspace dan memperbaiki kesalahan pengetikan sebelum berkomitmen pada nilai baru. Misalnya, properti TextBox.Text default ke nilai UpdateSourceTriggerLostFocus, yang menyebabkan nilai sumber berubah hanya ketika elemen kontrol kehilangan fokus, bukan ketika properti TextBox.Text diubah. Lihat halaman properti UpdateSourceTrigger untuk informasi tentang cara menemukan nilai default properti dependensi.

Tabel berikut ini menyediakan contoh skenario untuk setiap nilai UpdateSourceTrigger menggunakan TextBox sebagai contoh.

Nilai UpdateSourceTrigger Saat nilai sumber diperbarui Contoh skenario untuk TextBox
LostFocus (default untuk TextBox.Text) Saat kontrol TextBox kehilangan fokus. TextBox yang terkait dengan logika validasi (lihat Validasi Data di bawah).
PropertyChanged Saat Anda mengetik ke dalam TextBox. TextBox mengontrol di jendela ruang obrolan.
Explicit Saat aplikasi memanggil UpdateSource. TextBox mengontrol dalam formulir yang dapat diedit (memperbarui nilai sumber hanya saat pengguna menekan tombol kirim).

Misalnya, lihat Cara: Mengontrol kapan teks TextBox memperbarui sumber (.NET Framework).

Contoh pengikatan data

Untuk contoh pengikatan data, lihat UI aplikasi berikut dari Demo Pengikatan Data, yang menampilkan daftar item lelang.

Data binding sample screenshot

Aplikasi ini menunjukkan fitur pengikatan data berikut:

  • Konten ListBox terikat ke koleksi objek AuctionItem. Objek AuctionItem memiliki properti seperti Description, StartPrice, StartDate, Category, dan SpecialFeatures.

  • Data (objek AuctionItem ) yang ditampilkan di templat ListBox sehingga deskripsi dan harga saat ini ditampilkan untuk setiap item. Templat dibuat dengan menggunakan DataTemplate. Selain itu, tampilan setiap item tergantung pada nilai SpecialFeatures dari AuctionItem yang ditampilkan. Jika nilai SpecialFeatures dari AuctionItem adalah Color, item memiliki batas biru. Jika nilainya adalah Sorotan, item memiliki batas oranye dan bintang. Bagian Templat Data menyediakan informasi tentang templat data.

  • Pengguna dapat mengelompokkan, memfilter, atau mengurutkan data menggunakan CheckBoxes yang disediakan. Pada gambar di atas, Kelompokkan menurut kategori dan Urutkan menurut kategori dan tanggalCheckBoxes dipilih. Anda mungkin telah memperhatikan bahwa data dikelompokkan berdasarkan kategori produk, dan nama kategori dalam urutan alfabet. Sulit untuk memperhatikan dari gambar tetapi item juga diurutkan berdasarkan tanggal mulai dalam setiap kategori. Pengurutan dilakukan menggunakan tampilan koleksi. Bagian Pengikatan ke koleksi membahas tampilan koleksi.

  • Saat pengguna memilih item, item ContentControl akan menampilkan detail item yang dipilih. Pengalaman ini disebut Skenario detail master. Bagian Skenario detail master menyediakan informasi tentang jenis pengikatan ini.

  • Jenis properti StartDate adalah DateTime, yang mengembalikan tanggal yang menyertakan waktu hingga milidetik. Dalam aplikasi ini, pengonversi kustom telah digunakan sehingga string tanggal yang lebih pendek ditampilkan. Bagian Konversi data menyediakan informasi tentang pengonversi.

Saat pengguna memilih tombol Tambahkan Produk, formulir berikut muncul.

Add Product Listing page

Pengguna dapat mengedit bidang dalam formulir, mempratinjau daftar produk menggunakan panel pratinjau singkat atau terperinci, dan memilih Submit untuk menambahkan daftar produk baru. Pengaturan pengelompokan, pemfilteran, dan pengurutan yang ada akan berlaku untuk entri baru. Dalam kasus khusus ini, item yang dimasukkan dalam gambar di atas akan ditampilkan sebagai item kedua dalam kategori Komputer.

Tidak ditampilkan dalam gambar ini adalah logika validasi yang disediakan dalam Tanggal MulaiTextBox. Jika pengguna memasukkan tanggal yang tidak valid (format tidak valid atau tanggal yang sudah lewat), pengguna akan diberi tahu dengan ToolTip dan tanda seru merah di sebelah TextBox. Bagian Validasi Data membahas cara membuat logika validasi.

Sebelum masuk ke berbagai fitur pengikatan data yang diuraikan di atas, pertama kita akan membahas konsep dasar yang penting untuk memahami pengikatan data WPF.

Membuat pengikatan

Untuk memulihkan beberapa konsep yang dibahas di bagian sebelumnya, Anda membuat pengikatan menggunakan objek Binding, dan setiap pengikatan biasanya memiliki empat komponen: target pengikatan, properti target, sumber pengikatan, dan jalur ke nilai sumber yang akan digunakan. Bagian ini membahas cara menyiapkan pengikatan.

Sumber pengikatan terkait dengan DataContext aktif untuk elemen. Elemen secara otomatis mewarisi DataContext jika belum ditentukan secara eksplisit.

Pertimbangkan contoh berikut, di mana objek sumber pengikatan adalah kelas bernama MyData yang ditentukan dalam namespace layanan SDKSample. Untuk tujuan demonstrasi, MyData memiliki properti string bernama ColorName yang nilainya diatur ke "Merah". Dengan demikian, contoh ini menghasilkan tombol dengan latar belakang merah.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <DockPanel.DataContext>
        <Binding Source="{StaticResource myDataSource}"/>
    </DockPanel.DataContext>
    <Button Background="{Binding Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Untuk informasi selengkapnya tentang sintaks deklarasi pengikatan dan contoh cara menyiapkan pengikatan dalam kode, lihat Gambaran umum deklarasi pengikatan.

Jika kita menerapkan contoh ini ke diagram dasar kita, gambar yang dihasilkan terlihat seperti berikut ini. Gambar ini menjelaskan pengikatan OneWay karena properti Latar Belakang mendukung pengikatan OneWay secara default.

Diagram that shows the data binding Background property.

Anda mungkin bertanya-tanya mengapa pengikatan ini berfungsi meskipun properti ColorName berjenis string sementara properti Background berjenis Brush. Pengikatan ini menggunakan konversi jenis default, yang dibahas di bagian Konversi data.

Menentukan sumber pengikatan

Perhatikan bahwa dalam contoh sebelumnya, sumber pengikatan ditentukan dengan mengatur properti DockPanel.DataContext. Button kemudian mewarisi nilai DataContext dari DockPanel, yang merupakan elemen induknya. Untuk menegaskan kembali, objek sumber pengikatan adalah salah satu dari empat komponen pengikatan yang diperlukan. Jadi, tanpa objek sumber pengikatan yang ditentukan, pengikatan tidak akan melakukan apa pun.

Ada beberapa cara untuk menentukan objek sumber pengikatan. Menggunakan properti DataContext pada elemen induk berguna saat Anda mengikat beberapa properti ke sumber yang sama. Namun, terkadang mungkin lebih tepat untuk menentukan sumber pengikatan pada deklarasi pengikatan individu. Untuk contoh sebelumnya, alih-alih menggunakan properti DataContext, Anda dapat menentukan sumber pengikatan dengan mengatur properti Binding.Source langsung pada deklarasi pengikatan tombol, seperti dalam contoh berikut.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Selain mengatur properti DataContext pada elemen secara langsung, mewarisi nilai DataContext dari ancestor (seperti tombol pada contoh pertama), dan secara eksplisit menentukan sumber pengikatan dengan mengatur properti Binding.Source pada pengikatan ( seperti tombol contoh terakhir), Anda juga dapat menggunakan properti Binding.ElementName atau properti Binding.RelativeSource untuk menentukan sumber pengikatan. Properti ElementName ini berguna saat Anda mengikat elemen lain di aplikasi Anda, seperti saat Anda menggunakan penggeser untuk menyesuaikan lebar tombol. Properti RelativeSource berguna ketika pengikatan ditentukan dalam ControlTemplate atau Style. Untuk informasi selengkapnya, lihat Gambaran umum sumber pengikatan.

Menentukan jalur ke nilai

Jika sumber pengikatan Anda adalah objek, Anda menggunakan properti Binding.Path untuk menentukan nilai yang akan digunakan untuk pengikatan Anda. Jika Anda mengikat data XML, Anda menggunakan properti Binding.XPath untuk menentukan nilai. Dalam beberapa kasus, mungkin berlaku untuk menggunakan properti Path bahkan ketika data Anda adalah XML. Misalnya, jika Anda ingin mengakses properti Nama xmlNode yang dikembalikan (sebagai hasil dari kueri JalurX), Anda harus menggunakan properti Path selain properti XPath.

Untuk informasi selengkapnya, lihat properti Path dan XPath.

Meskipun kami telah menekankan bahwa ke nilai Path yang akan digunakan adalah salah satu dari empat komponen pengikatan yang diperlukan, dalam skenario yang ingin Anda ikat ke seluruh objek, nilai yang akan digunakan akan sama dengan objek sumber pengikatan. Dalam kasus tersebut, berlaku untuk tidak menentukan Path. Pertimbangkan contoh berikut.

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

Contoh di atas menggunakan sintaks pengikatan kosong: {Binding}. Dalam hal ini, ListBox mewarisi DataContext dari elemen DockPanel induk (tidak ditampilkan dalam contoh ini). Ketika jalur tidak ditentukan, defaultnya adalah mengikat ke seluruh objek. Dengan kata lain, dalam contoh ini, jalur telah ditinggalkan karena kami mengikat properti ItemsSource ke seluruh objek. (Lihat bagian Pengikatan ke koleksi untuk diskusi mendalam.)

Selain mengikat koleksi, skenario ini juga berguna ketika Anda ingin mengikat ke seluruh objek alih-alih hanya satu properti objek. Misalnya, jika objek sumber Anda berjenis String, Anda mungkin hanya ingin mengikat string itu sendiri. Skenario umum lainnya adalah ketika Anda ingin mengikat elemen ke objek dengan beberapa properti.

Anda mungkin perlu menerapkan logika kustom sehingga data bermakna bagi properti target terikat Anda. Logika kustom mungkin dalam bentuk pengonversi kustom jika konversi jenis default tidak ada. Lihat Konversi data untuk informasi tentang pengonversi.

Pengikatan dan BindingExpression

Sebelum masuk ke fitur lain dan penggunaan pengikatan data, ada baiknya untuk memperkenalkan kelas BindingExpression. Seperti yang telah Anda lihat di bagian sebelumnya, kelas Binding adalah kelas tingkat tinggi untuk deklarasi pengikatan; kelas ini menyediakan banyak properti yang memungkinkan Anda menentukan karakteristik pengikatan. Kelas terkait, BindingExpression, adalah objek dasar yang mempertahankan koneksi antara sumber dan target. Pengikatan berisi semua informasi yang dapat dibagikan di beberapa ekspresi pengikatan. BindingExpression adalah ekspresi instans yang tidak dapat dibagikan dan berisi semua informasi instans dari Binding.

Pertimbangkan contoh berikut, di mana myDataObject adalah instans kelas MyData, myBinding adalah objek sumber Binding, dan MyData merupakan kelas yang ditentukan yang berisi properti string bernama ColorName. Contoh ini mengikat konten teks myText, turunan dari TextBlock, ke ColorName.

// Make a new source
var myDataObject = new MyData();
var myBinding = new Binding("ColorName")
{
    Source = myDataObject
};

// Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding);
' Make a New source
Dim myDataObject As New MyData
Dim myBinding As New Binding("ColorName")
myBinding.Source = myDataObject

' Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding)

Anda dapat menggunakan objek myBinding yang sama untuk membuat pengikatan lain. Misalnya, Anda dapat menggunakan objek myBinding untuk mengikat konten teks kotak centang ke ColorName. Dalam skenario itu, akan ada dua instans BindingExpression berbagi objek myBinding.

Objek BindingExpression dikembalikan dengan memanggil objek GetBindingExpression yang terikat data. Artikel berikut menunjukkan beberapa penggunaan kelas BindingExpression:

Konversi data

Di bagian Buat pengikatan, tombol berwarna merah karena properti Background terikat ke properti string dengan nilai "Merah". Nilai string ini berfungsi karena pengonversi jenis ada pada jenis Brush untuk mengonversi nilai string menjadi Brush.

Menambahkan informasi ini ke gambar di bagian Buat pengikatan terlihat seperti ini.

Diagram that shows the data binding Default property.

Namun, bagaimana jika alih-alih memiliki properti jenis string objek sumber pengikatan Anda memiliki properti Color jenis Color? Dalam hal ini, agar pengikatan berfungsi, Anda harus terlebih dahulu mengubah nilai properti Color menjadi sesuatu yang diterima properti Background. Anda perlu membuat pengonversi kustom dengan mengimplementasikan antarmuka IValueConverter, seperti dalam contoh berikut.

[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color As Color = CType(value, Color)
        Return New SolidColorBrush(color)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

Lihat IValueConverter untuk informasi lebih lanjut.

Sekarang pengonversi kustom digunakan alih-alih konversi default, dan diagram kami terlihat seperti ini.

Diagram that shows the data binding custom converter.

Untuk mengulanginya, konversi default mungkin tersedia karena pengonversi jenis yang ada dalam jenis yang terikat. Perilaku ini akan bergantung pada pengonversi jenis mana yang tersedia di target. Jika ragu, buat pengonversi Anda sendiri.

Berikut ini adalah beberapa skenario umum di mana masuk akal untuk mengimplementasikan pengonversi data:

  • Data Anda harus ditampilkan secara berbeda, tergantung pada budaya. Misalnya, Anda mungkin ingin menerapkan pengonversi mata uang atau pengonversi tanggal/waktu kalender berdasarkan konvensi yang digunakan dalam budaya tertentu.

  • Data yang digunakan tidak selalu dimaksudkan untuk mengubah nilai teks properti, tetapi sebaliknya dimaksudkan untuk mengubah beberapa nilai lain, seperti sumber untuk gambar, atau warna atau gaya teks tampilan. Pengonversi dapat digunakan dalam instans ini dengan mengonversi pengikatan properti yang tampaknya tidak sesuai, seperti mengikat bidang teks ke properti Latar Belakang sel tabel.

  • Lebih dari satu kontrol atau beberapa properti kontrol terikat ke data yang sama. Dalam hal ini, pengikatan utama mungkin hanya menampilkan teks, sedangkan pengikatan lainnya menangani masalah tampilan tertentu tetapi masih menggunakan pengikatan yang sama dengan informasi sumber.

  • Properti target memiliki koleksi pengikatan, yang disebut MultiBinding. Untuk MultiBinding, Anda menggunakan kustom IMultiValueConverter untuk menghasilkan nilai akhir dari nilai pengikatan. Misalnya, warna dapat dihitung dari nilai merah, biru, dan hijau, yang dapat menjadi nilai dari objek sumber pengikatan yang sama atau berbeda. Lihat MultiBinding untuk contoh dan informasi.

Pengikatan ke koleksi

Objek sumber pengikatan dapat diperlakukan baik sebagai objek tunggal yang propertinya berisi data atau sebagai koleksi data objek polimorfik yang sering dikelompokkan bersama (seperti hasil kueri ke database). Sejauh ini kita hanya membahas pengikatan ke objek tunggal. Namun, mengikat pengumpulan data adalah skenario umum. Misalnya, skenario umum adalah menggunakan ItemsControl seperti ListBox, ListView, atau TreeView untuk menampilkan pengumpulan data, seperti di aplikasi yang ditampilkan di bagian Apa yang dimaksud dengan pengikatan data.

Untungnya, diagram dasar kami masih berlaku. Jika Anda mengikat ItemsControl ke koleksi, diagramnya akan terlihat seperti ini.

Diagram that shows the data binding ItemsControl object.

Seperti yang ditunjukkan dalam diagram ini, untuk mengikat ItemsControl ke objek koleksi, properti ItemsControl.ItemsSource adalah properti yang akan digunakan. Anda dapat menganggap ItemsSource sebagai konten ItemsControl. Pengikatannya adalah OneWay karena properti ItemsSource mendukung pengikatan OneWay secara default.

Cara mengimplementasikan koleksi

Anda dapat menghitung koleksi apa pun yang mengimplementasikan antarmuka IEnumerable. Namun, untuk menyiapkan pengikatan dinamis sehingga penyisipan atau penghapusan dalam koleksi memperbarui antarmuka pengguna secara otomatis, koleksi harus mengimplementasikan antarmuka INotifyCollectionChanged. Antarmuka ini mengekspos peristiwa yang harus dimunculkan setiap kali koleksi yang mendasar berubah.

WPF menyediakan kelas ObservableCollection<T>, yang merupakan implementasi bawaan dari pengumpulan data yang mengekspos antarmuka INotifyCollectionChanged. Untuk sepenuhnya mendukung transfer nilai data dari objek sumber ke target, setiap objek dalam koleksi Anda yang mendukung properti yang dapat diikat juga harus mengimplementasikan antarmuka INotifyPropertyChanged. Untuk informasi selengkapnya, lihat Gambaran umum sumber pengikatan.

Sebelum menerapkan koleksi Anda sendiri, pertimbangkan untuk menggunakan ObservableCollection<T> atau salah satu kelas koleksi yang ada, seperti List<T>, Collection<T>, dan BindingList<T>, di antara banyak lainnya. Jika Anda memiliki skenario lanjutan dan ingin menerapkan koleksi Anda sendiri, pertimbangkan untuk menggunakan IList, yang menyediakan koleksi objek non-generik yang dapat diakses secara individual oleh indeks, dan dengan demikian memberikan performa terbaik.

Tampilan koleksi

Setelah ItemsControl Anda terikat ke koleksi data, Anda mungkin ingin mengurutkan, memfilter, atau mengelompokkan data. Untuk melakukannya, Anda menggunakan tampilan koleksi, yang merupakan kelas yang mengimplementasikan antarmuka ICollectionView.

Apa yang dimaksud dengan tampilan koleksi?

Tampilan koleksi adalah lapisan di atas koleksi sumber pengikatan yang memungkinkan Anda menavigasi dan menampilkan koleksi sumber berdasarkan kueri pengurutan, filter, dan grup, tanpa harus mengubah koleksi sumber yang mendasarinya sendiri. Tampilan koleksi juga mempertahankan penunjuk ke item saat ini dalam koleksi. Jika koleksi sumber mengimplementasikan antarmuka INotifyCollectionChanged, perubahan yang diangkat oleh peristiwa CollectionChanged disebarluaskan ke tampilan.

Karena tampilan tidak mengubah koleksi sumber yang mendasar, setiap koleksi sumber dapat memiliki beberapa tampilan yang terkait dengannya. Misalnya, Anda mungkin memiliki koleksi objek Tugas. Dengan penggunaan tampilan, Anda dapat menampilkan data yang sama dengan cara yang berbeda. Misalnya, di sisi kiri halaman Anda mungkin ingin memperlihatkan tugas yang diurutkan berdasarkan prioritas, dan di sisi kanan, dikelompokkan menurut area.

Cara membuat tampilan

Salah satu cara untuk membuat dan menggunakan tampilan adalah dengan membuat instans objek tampilan secara langsung dan kemudian menggunakannya sebagai sumber pengikatan. Misalnya, pertimbangkan aplikasi demo pengikatan data yang ditampilkan di bagian Apa itu pengikatan data. Aplikasi ini diimplementasikan sedimikian rupa sehingga ListBox mengikat ke tampilan atas pengumpulan data alih-alih pengumpulan data secara langsung. Contoh berikut diekstrak dari aplikasi demo pengikatan data. Kelas CollectionViewSource adalah proksi XAML dari kelas yang mewarisi dari CollectionView. Dalam contoh khusus ini, Source tampilan terikat ke koleksi AuctionItems (jenis ObservableCollection<T>) dari objek aplikasi saat ini.

<Window.Resources>
    <CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

Sumber daya listingDataView kemudian berfungsi sebagai sumber pengikatan untuk elemen dalam aplikasi, seperti ListBox.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

Untuk membuat tampilan lain untuk koleksi yang sama, Anda dapat membuat instans CollectionViewSource lain dan memberinya nama x:Key yang berbeda.

Tabel berikut menunjukkan jenis data tampilan apa yang dibuat sebagai tampilan koleksi default atau menurut CollectionViewSource berdasarkan jenis koleksi sumber.

Jenis koleksi sumber Jenis tampilan koleksi Catatan
IEnumerable Jenis internal berdasarkan CollectionView Tidak dapat mengelompokkan item.
IList ListCollectionView Tercepat.
IBindingList BindingListCollectionView

Menggunakan tampilan default

Menentukan tampilan koleksi sebagai sumber pengikatan adalah salah satu cara untuk membuat dan menggunakan tampilan koleksi. WPF juga membuat tampilan koleksi default untuk setiap koleksi yang digunakan sebagai sumber pengikatan. Jika Anda mengikat langsung ke koleksi, WPF mengikat ke tampilan defaultnya. Tampilan default ini dibagikan oleh semua pengikatan ke koleksi yang sama, sehingga perubahan yang dilakukan pada tampilan default oleh satu kontrol atau kode terikat (seperti pengurutan atau perubahan pada penunjuk item saat ini, dibahas nanti) tercermin dalam semua pengikatan lain ke koleksi yang sama.

Untuk mendapatkan tampilan default, Anda menggunakan metode GetDefaultView. Misalnya, lihat Mendapatkan tampilan default pengumpulan data (.NET Framework).

Tampilan koleksi dengan ADO.NET DataTables

Untuk meningkatkan kinerja, tampilan koleksi untuk objek ADO.NET DataTable atau DataView mendelegasikan pengurutan dan pemfilteran ke DataView, yang menyebabkan pengurutan dan pemfilteran dibagikan di semua tampilan koleksi sumber data. Untuk mengaktifkan setiap tampilan koleksi untuk mengurutkan dan memfilter secara independen, inisialisasi setiap tampilan koleksi dengan objek DataView sendiri.

Pengurutan

Seperti disebutkan sebelumnya, tampilan dapat menerapkan urutan pengurutan ke koleksi. Karena ada dalam koleksi yang mendasar, data Anda mungkin atau mungkin tidak memiliki urutan yang relevan dan melekat. Tampilan atas koleksi memungkinkan Anda memberlakukan pesanan, atau mengubah urutan default, berdasarkan kriteria perbandingan yang Anda berikan. Karena ini adalah tampilan data berbasis klien, skenario umumnya adalah pengguna mungkin ingin mengurutkan kolom data tabular per nilai yang sesuai dengan kolom. Menggunakan tampilan, pengurutan berbasis pengguna ini dapat diterapkan, sekali lagi tanpa membuat perubahan apa pun pada koleksi yang mendasar atau bahkan harus meminta ulang untuk konten koleksi. Misalnya, lihat Mengurutkan kolom GridView saat header diklik (.NET Framework).

Contoh berikut menunjukkan logika pengurutan dari "Urutkan menurut kategori dan tanggal" CheckBox dari antarmuka pengguna aplikasi di bagian Apa yang dimaksud dengan pengikatan data.

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{
    // Sort the items first by Category and then by StartDate
    listingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}
Private Sub AddSortCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    ' Sort the items first by Category And then by StartDate
    listingDataView.SortDescriptions.Add(New SortDescription("Category", ListSortDirection.Ascending))
    listingDataView.SortDescriptions.Add(New SortDescription("StartDate", ListSortDirection.Ascending))
End Sub

Filter

Tampilan juga dapat menerapkan filter ke koleksi, sehingga tampilan hanya menampilkan subset tertentu dari koleksi lengkap. Anda dapat memfilter kondisi dalam data. Misalnya, seperti yang dilakukan oleh aplikasi di bagian Apa yang dimaksud dengan pengikatan data, "Tampilkan hanya penawaran" CheckBox berisi logika untuk memfilter item yang berharga $25 atau lebih. Kode berikut dijalankan untuk mengatur ShowOnlyBargainsFilter sebagai Filter pengendali peristiwa bila CheckBox tersebut dipilih.

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{
    if (((CheckBox)sender).IsChecked == true)
        listingDataView.Filter += ListingDataView_Filter;
    else
        listingDataView.Filter -= ListingDataView_Filter;
}
Private Sub AddFilteringCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    Dim checkBox = DirectCast(sender, CheckBox)

    If checkBox.IsChecked = True Then
        AddHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    Else
        RemoveHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    End If
End Sub

Pengendali peristiwa ShowOnlyBargainsFilter memiliki implementasi berikut.

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{
    // Start with everything excluded
    e.Accepted = false;

    // Only inlcude items with a price less than 25
    if (e.Item is AuctionItem product && product.CurrentPrice < 25)
        e.Accepted = true;
}
Private Sub ListingDataView_Filter(sender As Object, e As FilterEventArgs)

    ' Start with everything excluded
    e.Accepted = False

    Dim product As AuctionItem = TryCast(e.Item, AuctionItem)

    If product IsNot Nothing Then

        ' Only include products with prices lower than 25
        If product.CurrentPrice < 25 Then e.Accepted = True

    End If

End Sub

Jika Anda menggunakan salah satu kelas CollectionView secara langsung alih-alih CollectionViewSource, Anda akan menggunakan properti Filter untuk menentukan panggilan balik. Misalnya, lihat Memfilter Data dalam Tampilan (.NET Framework).

Pengelompokan

Kecuali untuk kelas internal yang melihat koleksi IEnumerable, semua tampilan koleksi mendukung pengelompokan, yang memungkinkan pengguna untuk mempartisi koleksi dalam tampilan koleksi ke dalam grup logis. Grup dapat secara eksplisit, di mana pengguna menyediakan daftar grup, atau implisit, di mana grup dihasilkan secara dinamis tergantung pada data.

Contoh berikut menunjukkan logika "Kelompokkan menurut kategori" CheckBox.

// This groups the items in the view by the property "Category"
var groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
' This groups the items in the view by the property "Category"
Dim groupDescription = New PropertyGroupDescription()
groupDescription.PropertyName = "Category"
listingDataView.GroupDescriptions.Add(groupDescription)

Untuk contoh pengelompokan lain, lihat Item Grup di ListView yang Mengimplementasikan GridView (.NET Framework).

Penunjuk item saat ini

Tampilan juga mendukung gagasan item saat ini. Anda dapat menavigasi objek dalam tampilan koleksi. Saat menavigasi, Anda memindahkan penunjuk item yang memungkinkan Anda mengambil objek yang ada di lokasi tertentu dalam koleksi. Misalnya, lihat Menavigasi objek dalam Data CollectionView (.NET Framework).

Karena WPF mengikat ke koleksi hanya dengan menggunakan tampilan (tampilan yang Anda tentukan, atau tampilan default koleksi), semua pengikatan ke koleksi memiliki penunjuk item saat ini. Saat mengikat ke tampilan, karakter garis miring ("/") dalam nilai Path menunjukkan item tampilan saat ini. Dalam contoh berikut, konteks data adalah tampilan koleksi. Baris pertama mengikat koleksi. Baris kedua mengikat ke item saat ini dalam koleksi. Baris ketiga mengikat properti Description item saat ini dalam koleksi.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />

Sintaks garis miring dan properti juga dapat ditumpuk untuk melintasi hierarki koleksi. Contoh berikut mengikat item saat ini dari koleksi bernama Offices, yang merupakan properti dari item saat ini dari koleksi sumber.

<Button Content="{Binding /Offices/}" />

Penunjuk item saat ini dapat dipengaruhi oleh pengurutan atau pemfilteran apa pun yang diterapkan ke koleksi. Mengurutkan mempertahankan penunjuk item saat ini pada item terakhir yang dipilih, tetapi tampilan koleksi sekarang direstrukturisasi di sekitarnya. (Mungkin item yang dipilih ada di awal daftar sebelumnya, tetapi sekarang item yang dipilih mungkin berada di suatu tempat di tengah.) Pemfilteran mempertahankan item yang dipilih jika pilihan tersebut tetap terlihat setelah pemfilteran. Jika tidak, penunjuk item saat ini diatur ke item pertama dari tampilan koleksi yang difilter.

Skenario pengikatan detail master

Gagasan item saat ini berguna tidak hanya untuk navigasi item dalam koleksi, tetapi juga untuk skenario pengikatan detail master. Pertimbangkan antarmuka pengguna aplikasi di bagian Apa yang dimaksud dengan pengikatan data lagi. Dalam aplikasi tersebut, pilihan dalam ListBox menentukan konten yang ditampilkan di ContentControl. Dengan kata lain, saat ListBox item dipilih, ContentControl menampilkan detail item yang dipilih.

Anda dapat menerapkan skenario detail master hanya dengan memiliki dua kontrol atau lebih yang terikat pada tampilan yang sama. Contoh berikut dari demo Pengikatan data menunjukkan markup ListBox dan yang ContentControl Anda lihat di UI aplikasi di bagian Apa itu pengikatan data.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}" 
                Margin="9,0,0,0"/>

Perhatikan bahwa kedua kontrol terikat ke sumber yang sama, sumber daya statis listingDataView (lihat definisi sumber daya ini di bagian Cara membuat tampilan). Pengikatan ini berfungsi karena ketika objek tunggal (dalam hal ini ContentControl) terikat ke tampilan koleksi, objek tersebut secara otomatis mengikat ke CurrentItem tampilan. Objek CollectionViewSource secara otomatis menyinkronkan mata uang dan pilihan. Jika kontrol daftar Anda tidak terikat ke objek CollectionViewSource seperti dalam contoh ini, maka Anda perlu mengatur properti IsSynchronizedWithCurrentItem ke true agar ini berfungsi.

Untuk contoh lain, lihat Mengikat ke koleksi dan menampilkan informasi berdasarkan pilihan (.NET Framework) dan Menggunakan pola detail master dengan data hierarkis (.NET Framework).

Anda mungkin telah memperhatikan bahwa contoh di atas menggunakan templat. Bahkan, data tidak akan ditampilkan seperti yang kita inginkan tanpa menggunakan templat (yang secara eksplisit digunakan oleh ContentControl dan yang secara implisit digunakan oleh ListBox). Kami sekarang beralih ke templat data di bagian berikutnya.

Templat data

Tanpa menggunakan templat data, antarmuka pengguna aplikasi kami di bagian Contoh pengikatan data akan terlihat seperti berikut ini:

Data Binding Demo without Data Templates

Seperti yang ditunjukkan pada contoh di bagian sebelumnya, kontrol ListBox dan ContentControl terikat ke seluruh objek koleksi (atau lebih khususnya, tampilan atas objek koleksi) dari AuctionItem. Tanpa instruksi khusus tentang cara menampilkan pengumpulan data, ListBox menampilkan representasi string dari setiap objek dalam koleksi yang mendasar, dan ContentControl menampilkan representasi string objek yang terikat.

Untuk mengatasi masalah tersebut, aplikasi menentukan DataTemplates. Seperti yang ditunjukkan pada contoh di bagian sebelumnya, ContentControl secara eksplisit menggunakan templat data detailsProductListingTemplate. Kontrol ListBox secara implisit menggunakan kerangka data berikut saat menampilkan objek AuctionItem dalam koleksi.

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="86"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
                       Text="{Binding Path=Description}"
                       Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType"
                           Text="{Binding Path=CurrentPrice}" 
                           Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
            <DataTrigger.Setters>
                <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
                <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
                <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
                <Setter Property="Padding" Value="5" TargetName="border" />
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Dengan penggunaan kedua DataTemplates tersebut, antarmuka pengguna yang dihasilkan adalah yang ditunjukkan di bagian Apa yang dimaksud dengan pengikatan data. Seperti yang Anda lihat dari cuplikan layar itu, selain memungkinkan Anda menempatkan data di kontrol Anda, DataTemplates memungkinkan Anda menentukan visual yang menarik untuk data Anda. Misalnya, DataTriggerdigunakan dalam DataTemplate di atas sehingga AuctionItem dengan nilai SpecialFeaturesHighLight akan ditampilkan dengan batas oranye dan sebuah bintang.

Untuk informasi selengkapnya tentang templat data, lihat Gambaran umum templat data (.NET Framework).

Validasi data

Sebagian besar aplikasi yang mengambil input pengguna harus memiliki logika validasi untuk memastikan bahwa pengguna telah memasukkan informasi yang diharapkan. Pemeriksaan validasi dapat didasarkan pada jenis, rentang, format, atau persyaratan khusus aplikasi lainnya. Bagian ini membahas cara kerja validasi data di WPF.

Mengaitkan aturan validasi dengan pengikatan

Model pengikatan data WPF memungkinkan Anda untuk mengaitkan ValidationRules dengan objek Anda Binding. Misalnya, contoh berikut mengikat TextBox ke properti bernama StartPrice dan menambahkan objek ExceptionValidationRule ke properti Binding.ValidationRules.

<TextBox Name="StartPriceEntryForm" Grid.Row="2"
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Objek ValidationRule memeriksa apakah nilai properti valid. WPF memiliki dua jenis objek ValidationRule bawaan:

Anda juga dapat membuat aturan validasi sendiri dengan mengambil dari kelas ValidationRule dan mengimplementasikan metode Validate. Contoh berikut menunjukkan aturan yang digunakan oleh Tambahkan Cantuman Produk "Tanggal Mulai" TextBox dari bagian Apa yang dimaksud dengan pengikatan data.

public class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Test if date is valid
        if (DateTime.TryParse(value.ToString(), out DateTime date))
        {
            // Date is not in the future, fail
            if (DateTime.Now > date)
                return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            // Date is not a valid date, fail
            return new ValidationResult(false, "Value is not a valid date.");
        }

        // Date is valid and in the future, pass
        return ValidationResult.ValidResult;
    }
}
Public Class FutureDateRule
    Inherits ValidationRule

    Public Overrides Function Validate(value As Object, cultureInfo As CultureInfo) As ValidationResult

        Dim inputDate As Date

        ' Test if date is valid
        If Date.TryParse(value.ToString, inputDate) Then

            ' Date is not in the future, fail
            If Date.Now > inputDate Then
                Return New ValidationResult(False, "Please enter a date in the future.")
            End If

        Else
            ' // Date Is Not a valid date, fail
            Return New ValidationResult(False, "Value is not a valid date.")
        End If

        ' Date is valid and in the future, pass
        Return ValidationResult.ValidResult

    End Function

End Class

StartDateEntryFormTextBox menggunakan FutureDateRule ini, seperti yang ditunjukkan pada contoh berikut.

<TextBox Name="StartDateEntryForm" Grid.Row="3"
         Validation.ErrorTemplate="{StaticResource validationTemplate}" 
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
                 Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Karena nilai UpdateSourceTrigger adalah PropertyChanged, mesin pengikat memperbarui nilai sumber pada setiap penekanan tombol, yang berarti juga memeriksa setiap aturan dalam koleksi ValidationRules pada setiap penekanan tombol. Kami membahas ini lebih lanjut di bagian Proses Validasi.

Memberikan umpan balik visual

Jika pengguna memasukkan nilai yang tidak valid, Anda mungkin ingin memberikan beberapa umpan balik tentang kesalahan pada antarmuka pengguna aplikasi. Salah satu cara untuk memberikan umpan balik tersebut adalah dengan mengatur Validation.ErrorTemplate properti terlampir ke ControlTemplate kustom. Seperti yang ditunjukkan pada subbagian sebelumnya, StartDateEntryFormTextBox menggunakan ErrorTemplate yang disebut validationTemplate. Contoh berikut menunjukkan definisi validasiTemplate.

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

Elemen AdornedElementPlaceholder menentukan di mana kontrol yang ditata harus ditempatkan.

Selain itu, Anda juga dapat menggunakan ToolTip untuk menampilkan pesan kesalahan. Baik StartDateEntryForm dan StartPriceEntryFormTextBoxmenggunakan gaya textStyleTextBox, yang membuat ToolTip yang menampilkan pesan kesalahan. Contoh berikut menunjukkan definisi textStyleTextBox. Properti Validation.HasError terlampir adalah true ketika satu atau beberapa pengikatan pada properti elemen terikat mengalami kesalahan.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
    <Setter Property="Foreground" Value="#333333" />
    <Setter Property="MaxLength" Value="40" />
    <Setter Property="Width" Value="392" />
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Dengan ErrorTemplate dan ToolTip kustom, StartDateEntryFormTextBox akan terlihat seperti berikut saat ada kesalahan validasi.

Data binding validation error for date

Jika Binding Anda memiliki aturan validasi terkait tetapi Anda tidak menetapkan ErrorTemplate pada kontrol terikat, ErrorTemplate default akan digunakan untuk memberi tahu pengguna saat ada kesalahan validasi. Defaultnya ErrorTemplate adalah templat kontrol yang menentukan batas merah di lapisan adorner. Dengan default ErrorTemplate dan ToolTip, antarmuka pengguna StartPriceEntryFormTextBox terlihat seperti berikut ini saat ada kesalahan validasi.

Data binding validation error for price

Untuk contoh cara menyediakan logika untuk memvalidasi semua kontrol dalam kotak dialog, lihat bagian Kotak Dialog Kustom dalam Gambaran umum kotak dialog.

Proses validasi

Validasi biasanya terjadi ketika nilai target ditransfer ke properti sumber pengikatan. Transfer ini terjadi pada pengikatan TwoWay dan OneWayToSource. Untuk mengulangi, apa yang menyebabkan pembaruan sumber bergantung pada nilai properti UpdateSourceTrigger, seperti yang dijelaskan di bagian Apa yang memicu pembaruan sumber.

Item berikut menjelaskan proses validasi. Jika kesalahan validasi atau jenis kesalahan lainnya terjadi kapan saja selama proses ini, prosesnya dihentikan:

  1. Mesin penjilidan memeriksa apakah ada objek ValidationRule kustom yang ditentukan yang ValidationStep diatur ke RawProposedValue untuk Binding itu, dalam hal ini ia memanggil metode Validate pada setiap ValidationRule hingga salah satunya berjalan menjadi kesalahan atau sampai semuanya lulus.

  2. Mesin pengikatan kemudian memanggil pengonversi, jika ada.

  3. Jika pengonversi berhasil, mesin penjilidan akan memeriksa apakah ada objek ValidationRule kustom yang ditentukan yang ValidationStep diatur ke ConvertedProposedValue untuk Binding tersebut, dalam hal ini ia memanggil metode Validate pada setiap ValidationRule yang memiliki ValidationStep diatur ke ConvertedProposedValue hingga salah satu dari mereka mengalami kesalahan atau sampai semuanya lulus.

  4. Mesin pengikatan mengatur properti sumber.

  5. Mesin penjilidan memeriksa apakah ada objek ValidationRule kustom yang ditentukan yang ValidationStep diatur ke UpdatedValue untuk Binding itu, dalam hal ini ia memanggil metode Validate pada setiap ValidationRule yang memiliki ValidationStep atur ke UpdatedValue sampai salah satu dari mereka mengalami kesalahan atau sampai semuanya lulus. Jika DataErrorValidationRule dikaitkan dengan pengikatan dan ValidationStep diatur ke default, UpdatedValue, DataErrorValidationRule dicentang pada saat ini. Pada titik ini semua pengikatan yang memiliki ValidatesOnDataErrors diatur ke true dicentang.

  6. Mesin penjilidan memeriksa apakah ada objek ValidationRule kustom yang ditentukan yang ValidationStep diatur ke CommittedValue untuk Binding itu, dalam hal ini ia memanggil metode Validate pada setiap ValidationRule yang memiliki ValidationStep atur ke CommittedValue sampai salah satu dari mereka mengalami kesalahan atau sampai semuanya lulus.

Jika ValidationRule tidak lulus setiap saat selama proses ini, mesin penjilidan akan membuat objek ValidationError dan menambahkannya ke koleksi Validation.Errors elemen terikat. Sebelum mesin penjilidan menjalankan objek ValidationRule pada langkah tertentu, mesin penjilidan akan menghapus ValidationError apa pun yang ditambahkan ke properti Validation.Errors yang dilampirkan dari elemen terikat selama langkah tersebut. Misalnya, jika ValidationRule yang ValidationStep diatur ke UpdatedValue gagal, saat berikutnya proses validasi terjadi, mesin pengikat menghapus ValidationError itu segera sebelum memanggil ValidationRule yang memiliki ValidationStep diatur ke UpdatedValue.

Saat Validation.Errors tidak kosong, Validation.HasError properti elemen yang dilampirkan diatur ke true. Selain itu, jika properti NotifyOnValidationError dari Binding diatur ke true, maka mesin pengikat akan memunculkan Validation.Error peristiwa terlampir pada elemen.

Perhatikan juga bahwa transfer nilai yang valid di kedua arah (target ke sumber atau sumber ke target) menghapus Validation.Errors properti terlampir.

Jika penjilidan memiliki ExceptionValidationRule yang terkait dengannya, atau properti ValidatesOnExceptions diatur ke true dan pengecualian dilemparkan saat mesin penjilidan mengatur sumbernya, mesin penjilidan akan memeriksa apakah ada UpdateSourceExceptionFilter. Anda dapat menggunakan panggilan balik UpdateSourceExceptionFilter untuk menyediakan handler kustom untuk menangani pengecualian. Jika UpdateSourceExceptionFilter tidak ditentukan pada Binding, mesin pengikat akan membuat ValidationError dengan pengecualian dan menambahkannya ke koleksi Validation.Errors elemen terikat.

Mekanisme debugging

Anda dapat mengatur properti PresentationTraceSources.TraceLevel terlampir pada objek terkait pengikatan untuk menerima informasi tentang status pengikatan tertentu.

Baca juga