Gambaran Umum Templat Data
Model templat data WPF memberi Anda fleksibilitas yang besar untuk menentukan presentasi data Anda. Kontrol WPF memiliki fungsionalitas bawaan untuk mendukung penyesuaian presentasi data. Topik ini pertama-tama menunjukkan cara menentukan DataTemplate dan kemudian memperkenalkan fitur templat data lainnya, seperti pemilihan templat berdasarkan logika kustom dan dukungan untuk tampilan data hierarkis.
Prasyarat
Topik ini berfokus pada fitur templat data dan bukan pengenalan konsep pengikatan data. Untuk informasi tentang konsep pengikatan data dasar, lihat Gambaran Umum Pengikatan Data.
DataTemplate adalah tentang presentasi data dan merupakan salah satu dari banyak fitur yang disediakan oleh model gaya dan templat WPF. Untuk pengenalan model gaya dan templat WPF, seperti cara menggunakan Style untuk mengatur properti pada kontrol, lihat topik Gaya dan Templat .
Selain itu, penting untuk memahami Resources
, yang pada dasarnya apa yang memungkinkan objek seperti Style dan DataTemplate dapat digunakan kembali. Untuk informasi selengkapnya tentang sumber daya, lihat Sumber Daya XAML.
Dasar-Dasar Templat Data
Untuk menunjukkan mengapa DataTemplate penting, mari kita telusuri contoh pengikatan data. Dalam contoh ini, kita memiliki ListBox yang terikat ke daftar Task
objek. Setiap Task
objek memiliki TaskName
(string), Description
(string), Priority
(int), dan properti jenis TaskType
, yang merupakan Enum
dengan nilai Home
dan Work
.
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample"
Title="Introduction to Data Templating Sample">
<Window.Resources>
<local:Tasks x:Key="myTodoList"/>
</Window.Resources>
<StackPanel>
<TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"/>
</StackPanel>
</Window>
Tanpa DataTemplate
DataTemplateTanpa , saat ini kami ListBox terlihat seperti ini:
Apa yang terjadi adalah bahwa tanpa instruksi tertentu, ListBox panggilan ToString
secara default saat mencoba menampilkan objek dalam koleksi. Oleh karena itu, jika Task
objek mengambil ToString
alih metode , maka ListBox menampilkan representasi string dari setiap objek sumber dalam koleksi yang mendasar.
Misalnya, jika Task
kelas mengambil alih metode dengan ToString
cara ini, di mana name
adalah bidang untuk TaskName
properti:
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
Kemudian terlihat ListBox seperti berikut ini:
Namun, itu membatasi dan tidak fleksibel. Selain itu, jika Anda mengikat data XML, Anda tidak akan dapat mengambil alih ToString
.
Menentukan DataTemplate Sederhana
Solusinya adalah mendefinisikan DataTemplate. Salah satu cara untuk melakukan itu adalah dengan mengatur ItemTemplate properti ke ListBoxDataTemplate. Apa yang Anda tentukan dalam DataTemplate menjadi struktur visual objek data Anda. Berikut ini DataTemplate cukup sederhana. Kami memberikan instruksi bahwa setiap item muncul sebagai tiga TextBlock elemen dalam .StackPanel Setiap TextBlock elemen terikat ke properti Task
kelas.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Data yang mendasar untuk contoh dalam topik ini adalah kumpulan objek CLR. Jika Anda mengikat data XML, konsep dasarnya sama, tetapi ada sedikit perbedaan sinaks. Misalnya, alih-alih memiliki Path=TaskName
, Anda akan mengatur XPath ke @TaskName
(jika TaskName
merupakan atribut dari simpul XML Anda).
Sekarang tampilan kita ListBox seperti berikut:
Membuat DataTemplate sebagai Sumber Daya
Dalam contoh di atas, kami mendefinisikan sebaris DataTemplate . Lebih umum untuk menentukannya di bagian sumber daya sehingga dapat menjadi objek yang dapat digunakan kembali, seperti dalam contoh berikut:
<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
Sekarang Anda dapat menggunakan myTaskTemplate
sebagai sumber daya, seperti dalam contoh berikut:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
Karena myTaskTemplate
merupakan sumber daya, Anda sekarang dapat menggunakannya pada kontrol lain yang memiliki properti yang mengambil DataTemplate jenis. Seperti yang ditunjukkan di atas, untuk ItemsControl objek, seperti ListBox, itu adalah ItemTemplate properti . Untuk ContentControl objek, ini adalah ContentTemplate properti .
Properti DataType
Kelas DataTemplate ini memiliki DataType properti yang sangat mirip TargetType dengan properti Style kelas. Oleh karena itu, alih-alih menentukan x:Key
untuk DataTemplate dalam contoh di atas, Anda dapat melakukan hal berikut:
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
Ini DataTemplate akan diterapkan secara otomatis ke semua Task
objek. Perhatikan bahwa dalam hal x:Key
ini diatur secara implisit. Oleh karena itu, jika Anda menetapkan nilai ini DataTemplatex:Key
, Anda mengesampingkan implisit x:Key
dan DataTemplate tidak akan diterapkan secara otomatis.
Jika Anda mengikat ContentControl ke kumpulan Task
objek, ContentControl jangan gunakan yang di atas DataTemplate secara otomatis. Ini karena pengikatan pada ContentControl kebutuhan informasi lebih lanjut untuk membedakan apakah Anda ingin mengikat seluruh koleksi atau objek individual. Jika Anda ContentControl melacak pilihan ItemsControl jenis, Anda dapat mengatur Path properti pengikatan ContentControl ke "/
" untuk menunjukkan bahwa Anda tertarik dengan item saat ini. Misalnya, lihat Mengikat ke Koleksi dan Menampilkan Informasi Berdasarkan Pilihan. Jika tidak, Anda perlu menentukan DataTemplate secara eksplisit dengan mengatur ContentTemplate properti .
Properti DataType ini sangat berguna ketika Anda memiliki CompositeCollection berbagai jenis objek data. Misalnya, lihat Menerapkan CompositeCollection.
Menambahkan Lainnya ke DataTemplate
Saat ini data muncul dengan informasi yang diperlukan, tetapi pasti ada ruang untuk perbaikan. Mari kita tingkatkan presentasi dengan menambahkan Border, , Griddan beberapa TextBlock elemen yang menjelaskan data yang sedang ditampilkan.
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="Aqua" BorderThickness="1"
Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border>
</DataTemplate>
Cuplikan ListBox layar berikut menunjukkan dengan yang dimodifikasi DataTemplateini :
Kita dapat mengatur HorizontalContentAlignment ke Stretch pada ListBox untuk memastikan lebar item mengambil seluruh ruang:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
Dengan properti diatur HorizontalContentAlignment ke Stretch, ListBox sekarang terlihat seperti ini:
Menggunakan DataTriggers untuk Menerapkan Nilai Properti
Presentasi saat ini tidak memberi tahu kami apakah merupakan Task
tugas rumah atau tugas kantor. Ingatlah bahwa Task
objek memiliki TaskType
properti jenis TaskType
, yang merupakan enumerasi dengan nilai Home
dan Work
.
Dalam contoh berikut, DataTrigger mengatur BorderBrush elemen border
bernama ke Yellow
jika TaskType
properti adalah TaskType.Home
.
<DataTemplate x:Key="myTaskTemplate">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=TaskType}">
<DataTrigger.Value>
<local:TaskType>Home</local:TaskType>
</DataTrigger.Value>
<Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Aplikasi kami sekarang terlihat seperti berikut ini. Tugas beranda muncul dengan batas kuning dan tugas kantor muncul dengan batas aqua:
Dalam contoh DataTrigger ini, menggunakan Setter untuk mengatur nilai properti. Kelas pemicu juga memiliki EnterActions properti dan ExitActions yang memungkinkan Anda memulai serangkaian tindakan seperti animasi. Selain itu, ada juga MultiDataTrigger kelas yang memungkinkan Anda menerapkan perubahan berdasarkan beberapa nilai properti terikat data.
Cara alternatif untuk mencapai efek yang sama adalah dengan mengikat BorderBrush properti ke TaskType
properti dan menggunakan pengonversi nilai untuk mengembalikan warna berdasarkan TaskType
nilai. Membuat efek di atas menggunakan pengonversi sedikit lebih efisien dalam hal performa. Selain itu, membuat konverter Anda sendiri memberi Anda lebih banyak fleksibilitas karena Anda menyediakan logika Anda sendiri. Pada akhirnya, teknik mana yang Anda pilih tergantung pada skenario dan preferensi Anda. Untuk informasi tentang cara menulis pengonversi, lihat IValueConverter.
Apa yang Termasuk dalam DataTemplate?
Dalam contoh sebelumnya, kami menempatkan pemicu dalam DataTemplate menggunakan DataTemplate.Triggers properti . Pemicu Setter menetapkan nilai properti elemen ( Border elemen) yang berada dalam DataTemplate. Namun, jika properti yang Anda Setters
khawatirkan bukan properti elemen yang berada dalam saat ini DataTemplate, mungkin lebih cocok untuk mengatur properti menggunakan Style yang untuk ListBoxItem kelas (jika kontrol yang Anda ikat adalah ListBox). Misalnya, jika Anda ingin menganimasikan TriggerOpacity nilai item saat mouse menunjuk ke item, Anda menentukan pemicu dalam ListBoxItem gaya. Misalnya, lihat Pengenalan Gaya dan Sampel Templat.
Secara umum, perlu diingat bahwa DataTemplate sedang diterapkan ke masing-masing yang dihasilkan ListBoxItem (untuk informasi selengkapnya tentang bagaimana dan di mana sebenarnya diterapkan, lihat ItemTemplate halaman.). Anda DataTemplate hanya memperhatikan presentasi dan tampilan objek data. Dalam kebanyakan kasus, semua aspek presentasi lainnya, seperti seperti apa item saat dipilih atau bagaimana ListBox tata letak item, tidak termasuk dalam definisi DataTemplate. Misalnya, lihat bagian Menata dan Membuat Templat ItemControl .
Memilih DataTemplate Berdasarkan Properti Objek Data
Di bagian Properti DataType, kami membahas bahwa Anda dapat menentukan templat data yang berbeda untuk objek data yang berbeda. Itu sangat berguna ketika Anda memiliki CompositeCollection berbagai jenis atau koleksi dengan item dari berbagai jenis. Di bagian Gunakan DataTriggers untuk Menerapkan Nilai Properti, kami telah menunjukkan bahwa jika Anda memiliki kumpulan jenis objek data yang sama, Anda dapat membuat DataTemplate lalu menggunakan pemicu untuk menerapkan perubahan berdasarkan nilai properti setiap objek data. Namun, pemicu memungkinkan Anda menerapkan nilai properti atau memulai animasi tetapi tidak memberi Anda fleksibilitas untuk membangun kembali struktur objek data Anda. Beberapa skenario mungkin mengharuskan Anda membuat yang berbeda DataTemplate untuk objek data dengan jenis yang sama tetapi memiliki properti yang berbeda.
Misalnya, ketika Task
objek memiliki Priority
nilai 1
, Anda mungkin ingin memberikan tampilan yang sama sekali berbeda untuk berfungsi sebagai pemberitahuan untuk diri Anda sendiri. Dalam hal ini, Anda membuat DataTemplate untuk tampilan objek berprioritas Task
tinggi. Mari kita tambahkan yang berikut ini DataTemplate ke bagian sumber daya:
<DataTemplate x:Key="importantTaskTemplate">
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="20"/>
</Style>
</DataTemplate.Resources>
<Border Name="border" BorderBrush="Red" BorderThickness="1"
Padding="5" Margin="5">
<DockPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Description}" />
<TextBlock>!</TextBlock>
</DockPanel>
</Border>
</DataTemplate>
Contoh ini menggunakan properti DataTemplate.Resources . Sumber daya yang ditentukan di bagian tersebut dibagikan oleh elemen dalam DataTemplate.
Untuk menyediakan logika untuk memilih mana yang DataTemplate akan digunakan berdasarkan Priority
nilai objek data, buat subkelas DataTemplateSelector dan ambil alih SelectTemplate metode . Dalam contoh berikut, SelectTemplate metode ini menyediakan logika untuk mengembalikan templat yang sesuai berdasarkan nilai Priority
properti. Templat yang akan dikembalikan ditemukan di sumber daya elemen amplop Window .
using System.Windows;
using System.Windows.Controls;
namespace SDKSample
{
public class TaskListDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate
SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null && item is Task)
{
Task taskitem = item as Task;
if (taskitem.Priority == 1)
return
element.FindResource("importantTaskTemplate") as DataTemplate;
else
return
element.FindResource("myTaskTemplate") as DataTemplate;
}
return null;
}
}
}
Namespace SDKSample
Public Class TaskListDataTemplateSelector
Inherits DataTemplateSelector
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Dim element As FrameworkElement
element = TryCast(container, FrameworkElement)
If element IsNot Nothing AndAlso item IsNot Nothing AndAlso TypeOf item Is Task Then
Dim taskitem As Task = TryCast(item, Task)
If taskitem.Priority = 1 Then
Return TryCast(element.FindResource("importantTaskTemplate"), DataTemplate)
Else
Return TryCast(element.FindResource("myTaskTemplate"), DataTemplate)
End If
End If
Return Nothing
End Function
End Class
End Namespace
Kita kemudian dapat mendeklarasikan TaskListDataTemplateSelector
sebagai sumber daya:
<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>
Untuk menggunakan sumber daya pemilih templat, tetapkan ke ItemTemplateSelector properti .ListBox Memanggil ListBox metode TaskListDataTemplateSelector
untuk setiap item dalam koleksi yang mendasarSelectTemplate. Panggilan meneruskan objek data sebagai parameter item. DataTemplate yang dikembalikan oleh metode kemudian diterapkan ke objek data tersebut.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
Dengan pemilih templat di tempat, ListBox sekarang muncul sebagai berikut:
Ini menyimpulkan diskusi kami tentang contoh ini. Untuk sampel lengkapnya, lihat Pengenalan Sampel Templat Data.
Menata dan Membuat Templat ItemControl
Meskipun bukan satu-satunya ItemsControl jenis kontrol yang dapat Anda gunakan DataTemplate dengan, ini adalah skenario yang sangat umum untuk mengikat ItemsControl ke koleksi. Di bagian Apa yang Termasuk dalam DataTemplate , kami membahas bahwa definisi Anda DataTemplate hanya perlu memperhatikan presentasi data. Untuk mengetahui kapan tidak cocok untuk menggunakan DataTemplate , penting untuk memahami berbagai gaya dan properti templat yang disediakan oleh ItemsControl. Contoh berikut dirancang untuk mengilustrasikan fungsi masing-masing properti ini. ItemsControl Dalam contoh ini terikat ke koleksi yang sama Tasks
seperti dalam contoh sebelumnya. Untuk tujuan demonstrasi, gaya dan templat dalam contoh ini semuanya dinyatakan sebaris.
<ItemsControl Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<!--The ItemsControl has no default visual appearance.
Use the Template property to specify a ControlTemplate to define
the appearance of an ItemsControl. The ItemsPresenter uses the specified
ItemsPanelTemplate (see below) to layout the items. If an
ItemsPanelTemplate is not specified, the default is used. (For ItemsControl,
the default is an ItemsPanelTemplate that specifies a StackPanel.-->
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="15">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<!--Use the ItemsPanel property to specify an ItemsPanelTemplate
that defines the panel that is used to hold the generated items.
In other words, use this property if you want to affect
how the items are laid out.-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--Use the ItemTemplate to set a DataTemplate to define
the visualization of the data objects. This DataTemplate
specifies that each data object appears with the Proriity
and TaskName on top of a silver ellipse.-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataTemplate.Resources>
<Grid>
<Ellipse Fill="Silver"/>
<StackPanel>
<TextBlock Margin="3,3,3,0"
Text="{Binding Path=Priority}"/>
<TextBlock Margin="3,0,3,7"
Text="{Binding Path=TaskName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--Use the ItemContainerStyle property to specify the appearance
of the element that contains the data. This ItemContainerStyle
gives each item container a margin and a width. There is also
a trigger that sets a tooltip that shows the description of
the data object when the mouse hovers over the item container.-->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Width" Value="100"/>
<Setter Property="Control.Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="True">
<Setter Property="Control.ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=Content.Description}"/>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Berikut ini adalah cuplikan layar contoh saat dirender:
Perhatikan bahwa alih-alih menggunakan ItemTemplate, Anda dapat menggunakan ItemTemplateSelector. Lihat bagian sebelumnya untuk contoh. Demikian pula, alih-alih menggunakan ItemContainerStyle, Anda memiliki opsi untuk menggunakan ItemContainerStyleSelector.
Dua properti terkait gaya lainnya yang ItemsControl tidak ditampilkan di sini adalah GroupStyle dan GroupStyleSelector.
Dukungan untuk Data Hierarkis
Sejauh ini kami hanya melihat cara mengikat dan menampilkan satu koleksi. Terkadang Anda memiliki koleksi yang berisi koleksi lain. Kelas HierarchicalDataTemplate ini dirancang untuk digunakan dengan HeaderedItemsControl jenis untuk menampilkan data tersebut. Dalam contoh berikut, ListLeagueList
adalah daftar League
objek. Setiap League
objek memiliki Name
dan kumpulan Division
objek. Masing-masing Division
memiliki Name
kumpulan Team
objek dan , dan setiap Team
objek memiliki Name
.
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="HierarchicalDataTemplate Sample"
xmlns:src="clr-namespace:SDKSample">
<DockPanel>
<DockPanel.Resources>
<src:ListLeagueList x:Key="MyList"/>
<HierarchicalDataTemplate DataType = "{x:Type src:League}"
ItemsSource = "{Binding Path=Divisions}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType = "{x:Type src:Division}"
ItemsSource = "{Binding Path=Teams}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Team}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</DockPanel.Resources>
<Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
<MenuItem Header="My Soccer Leagues"
ItemsSource="{Binding Source={StaticResource MyList}}" />
</Menu>
<TreeView>
<TreeViewItem ItemsSource="{Binding Source={StaticResource MyList}}" Header="My Soccer Leagues" />
</TreeView>
</DockPanel>
</Window>
Contoh menunjukkan bahwa dengan penggunaan HierarchicalDataTemplate, Anda dapat dengan mudah menampilkan data daftar yang berisi daftar lain. Berikut ini adalah cuplikan layar contoh.
Baca juga
.NET Desktop feedback