WPF 数据模板

数据模板和控件模板的区别在哪?

控件模板是用来修改某个控件内部的布局结构,不涉及到把一些数据(类对象)绑定到控件模板内部

数据模板是用来定义数据怎么显示,数据通常是来自数据集合,把一个数据集合和一个集合控件关联起来,如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>
View Code

引用定义好的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:

 结果:

 

posted @ 2022-08-24 16:18  薛定谔的小灯泡  阅读(1581)  评论(0编辑  收藏  举报