數據模板和控件模板的區別在哪?
控件模板是用來修改某個控件內部的布局結構,不涉及到把一些數據(類對象)綁定到控件模板內部
數據模板是用來定義數據怎么顯示,數據通常是來自數據集合,把一個數據集合和一個集合控件關聯起來,如ListBox,ComboBox,ItemContcrol這種集合形式的控件,數據怎么顯示其實也涉及到布局
可以簡單理解,只修改控件布局,則使用控件模板,如果想把一個集合控件關聯到一個數據集合並自定義顯示的方法,則使用數據模板
1.Datatemplate
舉個簡單的例子,定義一個集合控件ListBox,其中每個數據項是一個TextBlock,TextBlock的Text屬性的值 來自一個數據集合List<string>
先定義一個簡單的數據模板
Xaml代碼:
<ListBox ItemsSource="{Binding list}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
MainWindow代碼:
public partial class MainWindow : Window { public List<string> list { get; set; } = new List<string>(); public MainWindow() { InitializeComponent(); list.Add("111"); list.Add("222"); list.Add("333"); list.Add("444"); this.DataContext = this; } }
運行結果:
其中的邏輯關系如下:
每一個ListBoxitem對應一條數據。listboxitem是動態生成的,數據集合list中有多少條數據,listbox就會生成多少個listboxitem。
(當然如果想要實現添加數據,UI界面自動更新,需要使用帶有通知功能的ObservableCollection代替list)
重點是 為什么說數據模板可以定義數據怎么顯示?
還是上面的例子,我們定義了一個簡單的數據模板
<DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate>
這時候,每個ListboxItem實際是一個TextBlock,在數據集合list不做任何更改的情況下,我們可以修改數據模板,讓數據集合中的數據顯示成別的方式,
比如:把listboxitem改成button
數據模板代碼:
<ListBox ItemsSource="{Binding list}"> <ListBox.ItemTemplate> <DataTemplate> <!--<TextBlock Text="{Binding}"/>--> <Button Content="{Binding}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
運行結果:
在數據模板中可以做任何布局,從而實現讓數據有各種各樣的顯示形式
再比如:
<ListBox ItemsSource="{Binding list}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="每一行都是數據模板的數據"/> <Button Content="{Binding}"/> <Button Content="{Binding}"/> <TextBox Text="{ Binding Path=.}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
結果:
這就是數據模板的作用,可以讓數據以任意形式顯示。
一個地方需要理解一下:
對於一個集合控件,定義的數據模板是針對每個集合控件的子項來說的。是每個子項的數據模板。或者說,集合控件在生成子項時,根據定義好的數據模板去定義出每個子項。
復雜數據集合的例子
listbox和list<ClassName>數據集合
Book類代碼:
public class Book { public string BookName { get; set; } public string Title { get; set; } }
數據集合list2代碼:
public List<Book> list2 { get; set; } = new List<Book>(); public MainWindow() { InitializeComponent(); list2.Add(new Book() { BookName ="語文",Title="出師表"}); list2.Add(new Book() { BookName ="數學",Title="3.14"}); list2.Add(new Book() { BookName ="英語",Title="Hello"}); }
xaml代碼:
<ListBox ItemsSource="{Binding list2}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Border Background="DarkOliveGreen"> <TextBlock Text="{Binding BookName}"/> </Border> <Border Background="Aqua"> <TextBlock Text="{Binding Title}"/> </Border> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
運行結果:
對應關系:
每個ListBoxItem對應一個Book對象,每個Book對象包含2個屬性,ListBoxItem中怎么顯示Book對象的屬性,就是取決於我們如何處理數據模板了。
(對於Book類,如果需要通知功能,則需要繼承INotifyPropertyChanged並實現接口)
上面是把DataTemplate定義在控件內部,為了可復用,可抽離出來定義在資源或資源字典中

<Window.Resources> <DataTemplate x:Key="MyDataTemplate"> <StackPanel Orientation="Horizontal"> <Border Background="DarkOliveGreen"> <TextBlock Text="{Binding BookName}"/> </Border> <Border Background="Aqua"> <TextBlock Text="{Binding Title}"/> </Border> </StackPanel> </DataTemplate> </Window.Resources>
引用定義好的DataTemplate
<ListBox ItemsSource="{Binding list2}" ItemTemplate="{StaticResource MyDataTemplate}"/>
2.ItemPanelTemplate
繼續拿ListBox舉例,ListBox集合中的每個ListBoxItem是按照從上往下的排列順序,如果想要更改內部子項的布局順序,則需要修改ListBox的ItemPaneltemplate
<ItemsPanelTemplate x:Key="panel"> <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center"/> </ItemsPanelTemplate>
<ListBox ItemsSource="{Binding list2}" ItemTemplate="{StaticResource MyDataTemplate}" ItemsPanel="{StaticResource panel}"/>
3.ItemContainerStyle
修改每個ListboxItem的style。看一個簡單的例子,還是對上面的例子做拓展,每個子項使用一個Border包起來,並在下方添加一個CheckBox
<ListBox ItemsSource="{Binding list2}" ItemTemplate="{StaticResource MyDataTemplate}" ItemsPanel="{StaticResource panel}" > <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Blue"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <StackPanel> <Border Name="border" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" BorderBrush="Green" BorderThickness="1"> <ContentPresenter /> </Border> <CheckBox/> </StackPanel> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderThickness" Value="2" TargetName="border"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListBox.ItemContainerStyle> </ListBox>
運行結果:
Triggers:
結果: