第四章:数据/布局容器模板(二)

总目录



前言

本文不是新的章节,将会继续介绍数据绑定中的数据模板,以及布局容器模板的使用。


一、DataTemplate

在WPF中有三类模板相对来说还是比较重要:

  • ControlTemplate 控件模板
  • DataTemplate 数据模板
  • ItemsPanelTemplate 条目布局容器模板

这个三个模板经常时配合在一起使用,实现数据的最终呈现效果,控件模板我们上一章中介绍过,就是用于修改控件的外观的,那么数据模板的作用是什么?

1.通过实例理解数据模板

如果说,控件模板改变控件的外观,相当于在给控件穿“花装”。
那么,数据模板是描述数据的外观,就相当于是给数据穿“花装”。那么如何理解数据模板给数据穿“花装”呢?
下面我们通过案例来说明,现在我们有一个需求,我需要做一个待办事项的列表,使用ListBox完成。
首先我们定义待办对象

    //待办事项信息
    public class TaskInfo
    {
        //待办名称
        public string Name { get; set; }
        //待办描述
        public string Description { get; set; }
        //待办优先级
        public int Priority { get; set; }
        //待办类型
        public TaskType Type { get; set; }

        //public override string ToString()
        //{
        //    return $"待办事项:{Name} - 描述:{Description} - 优先级:{Priority}";
        //}
    }

    public enum TaskType
    {
        //家务
        Home,
        //工作
        Work
    }

然后定义一个ViewModel类用于数据绑定,并且初始化了一些数据

    public class MainViewModel
    {
        //待办列表
        public ObservableCollection<TaskInfo> TaskList { get; set; }

        public MainViewModel()
        {
            TaskList = new ObservableCollection<TaskInfo>()
            {
                new TaskInfo(){ Name="购物",Description="买菜,生活用品", Priority=2, Type= TaskType.Home },
                new TaskInfo(){ Name="写代码",Description="将还未完成的项目写完", Priority=1, Type= TaskType.Work },
                new TaskInfo(){ Name="打扫卫生",Description="将家里的卫生打扫一遍", Priority=2, Type= TaskType.Home },
                new TaskInfo(){ Name="洗衣服",Description="马上洗,不然明天没衣服穿了", Priority=1, Type= TaskType.Home },
            };
        }
    }

最后页面上绑定数据,ListBox绑定数据使用ItemsSource。

<Window x:Class="WpfDemo1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfDemo1"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainViewModel></local:MainViewModel>
    </Window.DataContext>
    <Grid>
        <ListBox  ItemsSource="{Binding TaskList}"></ListBox>
    </Grid>
</Window>

绑定上数据,我们发现,是这样的结果:
在这里插入图片描述
并没有达到我们预想的显示效果,于是呢,首先在TaskInfo内中我们重写了ToString()方法,以便可以显示我们想要的信息

        public override string ToString()
        {
            return $"待办事项:{Name} --- 描述:{Description} --- 优先级:{Priority}";
        }

重写之后,有了如下的显示
在这里插入图片描述
有了改进,但是全都挤到一起了,约束性太强了,还是没有自己想要的结果。那怎么才能自定义这个数据的显示格式呢?
想要解决这个问题啊,就就需要借助我们本文要介绍的数据模板,它的作用就是给数据“穿衣”,那我们下面就看看如何给数据“穿衣”吧!

        <ListBox  ItemsSource="{Binding TaskList}">
            <ListBox.ItemTemplate>
                <DataTemplate DataType="{x:Type local:TaskInfo}">
                    <Border Background="LightGray" CornerRadius="8" Padding="10" Margin="5">
                        <StackPanel>
                            <TextBlock Text="{Binding Path=Name}" />
                            <TextBlock Text="{Binding Path=Description}"/>
                            <TextBlock Text="{Binding Path=Priority}"/>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

“穿衣”后显示效果如下:
在这里插入图片描述
还可以写的更美观,不过上面这个简单的案例足以说明数据模板“穿衣”效果,另外注意,数据模板也是可以定义成资源,被重复使用的。接下来就详细讲解一下这个数据模板 !

2.数据模板详情

1、基本情况

(1)官方描述:描述数据对象的可视结构。
通过上面的案例我们就可以说:数据模板就是定义数据显示方式的模板。

(2)将数据模板创建为资源
如上面的案例中,我们是直接在ListBox内部定义了一个DataTemplate,日常使用中都是将DataTemplate定义在资源中,如:

    <Window.Resources>
        <DataTemplate x:Key="lb_dt" DataType="{x:Type local:TaskInfo}">
            <Border Background="LightGray" CornerRadius="8" Padding="10" Margin="5">
                <StackPanel>
                    <TextBlock Text="{Binding Path=Name}" />
                    <TextBlock Text="{Binding Path=Description}"/>
                    <TextBlock Text="{Binding Path=Priority}"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>

使用时,直接引用即可。

 <ListBox  ItemsSource="{Binding TaskList}" ItemTemplate="{StaticResource lb_dt}"></ListBox>

(3)DataType 属性
在上面案例中,你会发现在定义DataTemplate 的时候,还写了DataType="{x:Type local:TaskInfo}",这个属性则是为数据模板指定适用的对象,一般情况下都是需要指定的。

2、DataTemplate的使用场景

上面案例中,在ListBox使用的是ItemTemplate 属性,该属性是DataTemplate类型,那么还有那些控件的属性类型是DataTemplate呢?我们可以给那些控件设置DataTemplate呢?
具有DataTemplate类型的属性 元素有如下三类:

  • ContentControl 中的ContentTemplate属性 ,常用ContentControl有:Button、ButtonBase、CheckBox、ComboBoxItem、Label、ListBoxItem、ListViewItem、RadioButton、ToolTip、UserControl、Window等
  • ItemsControl 中的ItemTemplate属性(使用最多) ,常用的ItemsControl 有ComoBox、ListBox,ListView,Menu、MenuBase、ContextMenu
  • DataGrid中的DataGridTemplateColumn.CellTemplate属性

以上三种类型是我们使用DataTemplate的场景,其中对于ItemsControl 中的ItemTemplate 是使用的比较多的。

1)ContentControl 中的ContentTemplate实例

此处我们就以UserControl为例:
平常我们给Button 自定义模板的时候,均定义在资源中,这里为了方便举例写在内部:

        <Button Height="50" Width="250" Content="测试按钮" 
                Foreground="Red" Background="Green"
                BorderBrush="Red" >
            <Button.Template>
                <ControlTemplate TargetType="Button">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" 
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            Background="{TemplateBinding Background}">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"></ContentPresenter>
                    </Border>
                </ControlTemplate>
            </Button.Template>
        </Button>

我们知道,当我们在ContentControl 的ControlTemplate 使用ContentPresenter 的时候,ControlTemplate会自动将控件的 Content 属性绑定到 ContentPresenter。
既然ContentControl 的Content 属性是object 类型,可不是仅仅如上面的案例中显示一个文本而已。
下面我们给Button 定义一个数据模板:

  <Button Height="50" Width="250">
            <Button.Template>
                <ControlTemplate TargetType="Button">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="10" Background="{TemplateBinding Background}">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" >
                            <ContentPresenter.ContentTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Ellipse Height="20" Width="20" Fill="Red"></Ellipse>
                                        <TextBlock Text="测试按钮1" Margin="10"></TextBlock>
                                        <Ellipse Height="20" Width="20" Fill="Red"></Ellipse>
                                    </StackPanel>
                                </DataTemplate>
                            </ContentPresenter.ContentTemplate>
                        </ContentPresenter>
                    </Border>
                </ControlTemplate>
            </Button.Template>
        </Button>

效果如下:
在这里插入图片描述
使用ContentPresenter的ContentTemplate属性,可以在ContentTemplate定义比较复杂的数据,而不仅仅是文本。
但是通常我们将DataTemplate 定义为资源,因此一般的如下编写:

    <Window.Resources>
        <ControlTemplate TargetType="Button" x:Key="btn_ct">
            <Border BorderBrush="{TemplateBinding BorderBrush}" 
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            Background="{TemplateBinding Background}">
                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"></ContentPresenter>
            </Border>
        </ControlTemplate>

        <DataTemplate DataType="Button" x:Key="btn_dt">
            <StackPanel Orientation="Horizontal">
                <Ellipse Height="20" Width="20" Fill="Red"></Ellipse>
                <TextBlock Text="测试按钮1" Margin="10"></TextBlock>
                <Ellipse Height="20" Width="20" Fill="Red"></Ellipse>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
<Button Height="50" Width="250" 
		Template="{StaticResource btn_ct}" 
		ContentTemplate="{StaticResource btn_dt}"/>

从这里对模板的引用我们其实更能从中体会出控件模板与数据模板的具体使用了。
如上案例,在Button的ControlTemplate中确定好ContentPresenter显示位置,然后由DataTemplate 决定Content中的数据具体如何显示出来。

其实以上案例,不是特别的恰当,但是足以说明ContentControl 的ControlTemplate 的使用,一般在ContentControl 中使用ControlTemplate 属性,不是很多。多数还是使用UserControl做详情页的时候,可以通过给UserControl的定义一个数据模板,决定每一项数据的详情该如何在UserControl中展示。这个具体到UserControl那一章再详细讲解。

2)ItemsControl 中的ItemTemplate实例

上面介绍完了ContentControl 的ControlTemplate 的实例,这里我们就需要重点介绍ItemsControl 中的ItemTemplate属性了,这个太常见了。
其实通过最开始的待办列表的那个案例中,应该就能感受到ItemsControl 中的ItemTemplate 是用于定义这类条目控件的每一个子项中的显示方式。如果是待办列表,那么ItemTemplate 就是定义每一个待办事项的信息,如果是一个用户列表啊,那么ItemTemplate 中应该定义的就是每一个用户的相关信息。

另外如同在ContentControl 的ControlTemplate 中使用ContentPresenter,如果在 ItemsControl 的 ControlTemplate中声明了 ItemsPresenter,ControlTemplate会自动将控件绑定到 ItemTemplate 和 Items 属性。
接着上面的待办案例介绍:

    <Window.Resources>
        <ControlTemplate x:Key="lb_ct" TargetType="ListBox">
            <Border CornerRadius="10" Background="LightBlue">
                <StackPanel Orientation="Vertical">
                    <TextBlock margin="10" Text="待办列表:"></TextBlock>
                    <ItemsPresenter></ItemsPresenter>
                </StackPanel>
            </Border>
        </ControlTemplate>
        <DataTemplate x:Key="lb_dt" DataType="{x:Type local:TaskInfo}" >
            <Border Background="LightGray" CornerRadius="8" Padding="10" Margin="5">
                <StackPanel>
                    <TextBlock Text="{Binding Path=Name}" />
                    <TextBlock Text="{Binding Path=Description}"/>
                    <TextBlock Text="{Binding Path=Priority}"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>

以上代码将ControlTemplate 和ControlTemplate 都定义在资源中,然后ControlTemplate 中定义了ListBox的控件模板,并且在模板中使用ItemsPresenter确定了子项的呈现位置,最后在DataTemplate定义了子项数据的呈现方式。

        <ListBox Width="200" 
                 ItemsSource="{Binding TaskList}" 
                 ItemTemplate="{StaticResource lb_dt}" 
                 Template="{StaticResource lb_ct}"></ListBox>

在这里插入图片描述

另外需要注意: 当我们定义x:key的时候,尽量写在定义属性的第一个位置,否则编译器可能会报出找不到资源的错误, 如<ControlTemplate x:Key="lb_ct" TargetType="ListBox">,和<DataTemplate x:Key="lb_dt" DataType="{x:Type local:TaskInfo}" >均接着ControlTemplate 和DataTemplate 就定义key。

3)DataGrid中的DataGridTemplateColumn.CellTemplate实例

直接看实例,实例中使用DataGrid展示用户信息,并且有操作按钮
首先准备相关的对象和初始数据

    public class CellTemplateViewModel
    {
        private string title;

        public CellTemplateViewModel()
        {
            title = "用户信息列表";
            Users = new ObservableCollection<UserInfo>()
            {
                new UserInfo(){ Name="孙悟空",Phone="11111111111", Age=12000 },
                new UserInfo(){ Name="猪八戒",Phone="22222222222", Age=123400 },
                new UserInfo(){ Name="沙悟净",Phone="33333333333", Age=22000 },
            };
        }

        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        public ObservableCollection<UserInfo> Users { get; set; }

    }


    public class UserInfo
    {
        //姓名
        public string Name { get; set; }
        //年龄
        public int Age { get; set; }
        //手机
        public string Phone  { get; set; }
    }

然后绑定到页面中,

<Window x:Class="WpfDemo1.CellTemplateView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfDemo1"
        Title="CellTemplateView" Height="450" Width="800">
    <Window.DataContext>
        <local:CellTemplateViewModel></local:CellTemplateViewModel>
    </Window.DataContext>
    <StackPanel>
        <TextBlock Text="{Binding Title}"></TextBlock>
        <DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False"  CanUserAddRows="False" CanUserSortColumns="True">
            <DataGrid.Columns>
                <DataGridTextColumn Width="Auto" Binding="{Binding Name}" Header="姓名"/>
                <DataGridTextColumn Width="Auto" Binding="{Binding Age}" Header="年龄"/>
                <DataGridTextColumn Width="Auto" Binding="{Binding Phone}" Header="手机号"/>
                <DataGridTemplateColumn Header="操作">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Button Content="删除" Margin="5"></Button>
                                <Button Content="修改" Margin="5"></Button>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
</Window>

效果如下:
在这里插入图片描述

3.在数据模板中使用DataTrigger

我们这里还是以待办列表的案例来说讲解,比如现在待办列表的需求发生了改变,当待办列表的类型为Home的时候,边框为黄色,当待办事项为Work的时候为红色
只需将原先的代码做如下修改:

        <DataTemplate x:Key="lb_dt"  DataType="{x:Type local:TaskInfo}">
            <Border x:Name="border" Background="LightGray" CornerRadius="8" Padding="10" Margin="5">
                <StackPanel>
                    <TextBlock Text="{Binding Path=Name}" />
                    <TextBlock Text="{Binding Path=Description}"/>
                    <TextBlock Text="{Binding Path=Priority}"/>
                </StackPanel>
            </Border>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=Type}" >
                    <DataTrigger.Value>
                        <local:TaskType>Home</local:TaskType>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderThickness" Value="2"></Setter>
                    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=Type}" >
                    <DataTrigger.Value>
                        <local:TaskType>Work</local:TaskType>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderThickness" Value="3"></Setter>
                    <Setter TargetName="border" Property="BorderBrush" Value="Red"></Setter>
                </DataTrigger>
                
            </DataTemplate.Triggers>
        </DataTemplate>

效果如下:
在这里插入图片描述

4.DataTemplateSelector 数据模板选择器

我们上面通过定义数据触发器,将待办列表的边框根据类型进行了区分,现在呢,需求又改变了,我想要对于优先级为1 的数据模板和其他优先级的数据数据模板不一样。那这个时候应该怎么实现呢?

此时我们就需要用上DataTemplateSelector ,我们可以通过重写SelectTemplate方法即可实现,具体操作如下:
首先增加一个优先级为1 时的数据模板

<DataTemplate x:Key="lb_dt_important"  DataType="{x:Type local:TaskInfo}">
            <Border x:Name="border" Background="LightGray" CornerRadius="8" Padding="10" Margin="5">
                <StackPanel>
                    <TextBlock FontSize="20" Foreground="Red">
                        <TextBlock Text="{Binding Path=Priority}" />
                        <TextBlock Text="{Binding Path=Name}" />
                    </TextBlock>
                    <TextBlock Text="{Binding Path=Description}"/>
                </StackPanel>
            </Border>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=Type}" >
                    <DataTrigger.Value>
                        <local:TaskType>Home</local:TaskType>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderThickness" Value="2"></Setter>
                    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=Type}" >
                    <DataTrigger.Value>
                        <local:TaskType>Work</local:TaskType>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderThickness" Value="3"></Setter>
                    <Setter TargetName="border" Property="BorderBrush" Value="Red"></Setter>
                </DataTrigger>

            </DataTemplate.Triggers>
        </DataTemplate>

然后自定义一个数据模板选择器,重写SelectTemplate 方法

    public class TaskListDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;

            if (element != null && item != null && item is TaskInfo)
            {
                TaskInfo taskitem = item as TaskInfo;

                if (taskitem.Priority == 1)//这里通过值进行判断
                    return
                        element.FindResource("lb_dt_important") as DataTemplate;
                else
                    return
                        element.FindResource("lb_dt") as DataTemplate;
            }
            return null;
        }
    }

最后在页面中定义为资源,在需要的地方引用即可。

 <Window.Resources>
        <local:TaskListDataTemplateSelector x:Key="taskListDataTemplateSelector"></local:TaskListDataTemplateSelector>
   </Window.Resources>

在ListBox的ItemTemplateSelector 属性中引用数据模板选择器资源即可

<ListBox Width="200" Margin="10"
                 ItemsSource="{Binding TaskList}" 
                 Template="{StaticResource lb_ct}"               
                 ItemTemplateSelector="{StaticResource taskListDataTemplateSelector}">
</ListBox>

效果如下:
在这里插入图片描述
模板选择器还有一种用法如下:
在这里插入图片描述
这种用法相对于上面那种用法更为简洁明了!

二 、ItemsPanelTemplate

官方解释 :指定 ItemsPanelTemplate 用于项目布局的面板

下面还是以待办列表案例说明,将代码做如下修改:

<ListBox Width="500" Margin="10"
                 ItemsSource="{Binding TaskList}" 
                 Template="{StaticResource lb_ct}"               
                 ItemTemplateSelector="{StaticResource taskListDataTemplateSelector}">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel></WrapPanel>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
</ListBox>

以上代码是将ListBox默认的StackPanel布局方式,修改为WrapPanel,效果如下:
在这里插入图片描述
我们发现啊,布局方式变了。

其实 每种 ItemsControl 类型都有一个默认值 ItemsPanelTemplate。
一般ListBox 默认的ItemsPanelTemplate 指定为StackPanel布局面板,子项会依照StackPanel的规则进行排列, MenuItem,默认值使用 WrapPanel。 对于 StatusBar,默认值使用 DockPanel。

另外需要注意:ItemsPanelTemplate 必须包含Panel 。即是说只有Panel或者派生自Panel的布局容器才可以放入ItemsPanelTemplate 中。我们常见的布局控件Grid,Canvas,DockPanel,UniformGrid,StackPanel,WrapPanel都是属于Panel。

当然我们还可自定义继承于Panel的布局容器,这种自定义的容易也是可以放入ItemsPanelTemplate 中的,至于自定义布局容器,将会在自定义控件部分深入研究。

三、对 ItemsControl 进行样式设置和模板化

ItemsControl 是ListBox,ComboBox等条目控件的基类,这里将通过ItemsControl 对前面介绍的样式,控件模板,数据模板等知识点进行一个综合的应用。
同样以待办列表的数据源TaskList 作为ItemsControl 的数据源

<Window x:Class="WpfDemo1.ItemControlView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfDemo1"
        Title="ItemControlView" Height="450" Width="800">
    <Window.DataContext>
        <local:MainViewModel></local:MainViewModel>
    </Window.DataContext>
    <Window.Resources>
        <!-- 使用控件模板定义ItemsControl的控件外观 -->
        <ControlTemplate x:Key="TasksIcControlTemplate" TargetType="ItemsControl">
            <Border BorderBrush="Aqua" BorderThickness="2" CornerRadius="10">
                <StackPanel>
                    <TextBlock FontSize="24" Margin="10" Text="待办事项列表"></TextBlock>
                    <Rectangle Fill="Gray" Height="1" Margin="2"></Rectangle>
                    <ItemsPresenter/>
                </StackPanel>
            </Border>
        </ControlTemplate>
        <!-- 使用布局容器模板定义 ItemsControl子项的布局方式-->
        <ItemsPanelTemplate x:Key="TasksIcItemsPanelTemplate" >
            <WrapPanel />
        </ItemsPanelTemplate>
        <!--使用DataTemplate 定义 子项数据的显示方式-->
        <DataTemplate x:Key="TasksIcDataTemplate" DataType="{x:Type local:TaskInfo}">
            <DataTemplate.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="FontSize" Value="18"/>
                    <Setter Property="HorizontalAlignment" Value="Left"/>
                </Style>
            </DataTemplate.Resources>
            <Grid>
                <Border x:Name="border" BorderBrush="Green" BorderThickness="2" CornerRadius="10" Background="Aqua"></Border>
                <StackPanel >
                    <TextBlock Text="{Binding Priority,StringFormat=优  先  级 :{0}}" Margin="10"></TextBlock>
                    <TextBlock Text="{Binding Name,StringFormat=待办事项 :{0}}" Margin="10"/>
                    <TextBlock Text="{Binding Description,StringFormat=详情信息 :{0}}" Margin="10"/>
                </StackPanel>
            </Grid>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Priority}" Value="1">
                    <Setter TargetName="border" Property="BorderBrush" Value="Red"></Setter>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
        <!--使用TasksIcItemContainerStyle设置每一个子项的样式,并且通过样式给子项增加触发器-->
        <Style x:Key="TasksIcItemContainerStyle">
            <Setter Property="Control.Width" Value="350"/>
            <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>
    </Window.Resources>
    <Grid>
        <ItemsControl Margin="10"  ItemsSource="{Binding TaskList}"
                      Template="{StaticResource TasksIcControlTemplate}" 
                      ItemsPanel="{StaticResource TasksIcItemsPanelTemplate}"
                      ItemTemplate="{StaticResource TasksIcDataTemplate}"
                      ItemContainerStyle="{StaticResource TasksIcItemContainerStyle}">
        </ItemsControl>
    </Grid>
</Window>

效果如下:
在这里插入图片描述
另外也可以在上案例中使用ItemTemplateSelector代替ItemTemplate实现更丰富的数据模板选择。 同样,可以选择使用 ItemContainerStyleSelector代替使用 ItemContainerStyle。

四、HierarchicalDataTemplate分层数据模板

HierarchicalDataTemplate适用于展示带有层级的数据,比如年级/班级/小组/学生 这类可能是一层包一层的关系数据。

HierarchicalDataTemplate 类专用于 HeaderedItemsControl 类型以显示此类数据,即是说WPF提供了HeaderedItemsControl 类型的控件元素用于展示多层级的数据。常见的HeaderedItemsControl 类型控件有TreeViewItem 和 MenuItem

下面就以实例说明:

    public class HierarchicalDtTestViewModel
    {
        public HierarchicalDtTestViewModel()
        {
            GradeList = new List<Grade>()
            {
                
                new Grade ()
                { 
                    Name="一年级",
                    ClassList=new List<Class>()
                    {   
                        new Class ()
                        { 
                            Name="一班", 
                            GroupList=new List<Group> ()
                            {
                                new Group(){ Name="1组"},
                                new Group(){ Name="2组"},
                                new Group(){ Name="3组"},
                            },
                         },
                        new Class ()
                        {
                            Name="二班",
                            GroupList=new List<Group> ()
                            {
                                new Group(){ Name="1组"},
                                new Group(){ Name="2组"},
                                new Group(){ Name="3组"},
                            },
                         },
                    } 
                },

                new Grade ()
                {
                    Name="二年级",
                    ClassList=new List<Class>()
                    {
                        new Class ()
                        {
                            Name="1班",
                            GroupList=new List<Group> ()
                            {
                                new Group(){ Name="11组"},
                                new Group(){ Name="22组"},
                                new Group(){ Name="33组"},
                            },
                         },
                        new Class ()
                        {
                            Name="2班",
                            GroupList=new List<Group> ()
                            {
                                new Group(){ Name="11组"},
                                new Group(){ Name="22组"},
                                new Group(){ Name="33组"},
                            },
                         },
                    }
                },
            };
        }

        //年级列表
        public List<Grade> GradeList { get; set; }
    }

    public class Grade
    {
        public string Name { get; set; }

        public List<Class> ClassList { get; set; }
    }

    public class Class
    {
        public string Name { get; set; }

        public List<Group> GroupList { get; set; }
    }

    public class Group
    {
        public string Name { get; set; }
    }
<Window x:Class="WpfDemo1.HierarchicalDtTestView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfDemo1"
        Title="HierarchicalDtTestView" Height="450" Width="800">
    <Window.DataContext>
        <local:HierarchicalDtTestViewModel></local:HierarchicalDtTestViewModel>
    </Window.DataContext>
    <Window.Resources>
        <!--这里是指定 年级的数据模板-->
        <HierarchicalDataTemplate DataType="{x:Type local:Grade}" ItemsSource="{Binding ClassList}">
            <StackPanel Orientation="Horizontal" >
                <CheckBox ></CheckBox>
                <TextBlock Text="{Binding Name}"></TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>
        <!--这里是指定 班级的数据模板-->
        <HierarchicalDataTemplate DataType="{x:Type local:Class}" ItemsSource="{Binding GroupList}">
            <TextBlock Text="{Binding Name}"></TextBlock>
        </HierarchicalDataTemplate>
        <!--这里是指定 小组的数据模板-->
        <HierarchicalDataTemplate DataType="{x:Type local:Group}">
            <TextBlock Text="{Binding Name}"></TextBlock>
        </HierarchicalDataTemplate>
    </Window.Resources>
    
    <DockPanel>
        <Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
            <MenuItem Header="学校信息"
                  ItemsSource="{Binding GradeList}" />
        </Menu>
        <TreeView>
            <TreeViewItem ItemsSource="{Binding GradeList}" Header="学校信息" />
        </TreeView>
    </DockPanel>
</Window>

在这里插入图片描述
另外,数据模板中指定DataType尽量都是用这类形式:DataType="{x:Type local:Group}"


总结

以上就是今天要介绍的内容,有了样式,控件模板,数据绑定,数据模板等知识,对于我们后续写自定义控件以及掌握MVVM是很有帮助的。希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考:
官方资料-实现绑定验证
WPF Template模版之DataTemplate与ControlTemplate的关系和应用【二】

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值