WPF进阶技巧和实战03-控件(5-列表、树、网格02)
数据模板
样式提供了基本的格式化能力,但是不管如何修改ListBoxItem,他都不能够展示功能更强大的元素组合,因为了每个ListBoxItem只支持单个绑定字段(通过DisplayMemberPath属性设置),不可能包含多个字段或者图像的富列表。
数据模板就是这样一个能够突破这个最大限制,允许组合使用来自绑定对象的多个属性,以特定的方式排列他们并显示简单字符串的高级样式。
数据模板是一个定义如何显示绑定的数据对象的XAML标记,有两类控件支持数据模板:
- 内容控件:通过ContentTemplate属性支持数据模板。用于显示任何放置在Content属性中的内容(在基类ContentControl中定义了DataTemplate类型的对象ContentTemplate)
- 列表控件(继承自ItemsControl类的控件):通过ItemTemplate属性支持数据模板。这个模板用于显示作为ItemsSource提供的集合中的每个项(或者DataTable的每一行)
基于列表的模板特性实际上时一内容控件模板为基础。列表中每一项均由内容控件封装(ListBox的ListBoxItem,ComboBox的ComboBoxItem都是内容控件),不管列表的ItemTemplate(DataTemplate类型)属性指定什么样的模板,模板都被用做列表的每项的ContentTemplate属性
<ListBox Margin="7,3,7,10" HorizontalContentAlignment="Stretch" SnapsToDevicePixels="True">
<ListBox.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0" />
<Style.Triggers>
<Trigger Property="ListBoxItem.IsSelected" Value="True">
<Setter Property="ListBoxItem.Background" Value="DarkRed" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0" Background="White">
<Border Margin="5"
Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=Background}"
BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontWeight="Bold" Text="{Binding Path=ModelNumber}" />
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}" />
</Grid>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
分离和重用模板
与样式类似,通常也将模板声明为窗口或者应用程序的资源。在模板上增加键名,然后可以通过StaticResource引用来为列表添加数据模板。如果希望在不同类型的控件中自动重用相同的模板,可以通过设置DataTemplate.DataType属性来确定使用模板的绑定数据类型,并且删除资源键。
<DataTemplate DataType="{x:Type data:Product}">
<Border Margin="3" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}" />
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}" />
</Grid>
</Border>
</DataTemplate>
现在这个模板将用于窗口中任何绑定到Product对象的列表控件或者内容控件,二不需要指定ItemTemplate设置。
使用更高级的模板
模板可以包含非常丰富的内容,可以使用更复杂的控件、关联事件处理程序、将数据转换成不同的表达形式以及使用动画等。下面的例子使用的转换器和事件关联。
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0" Background="White">
<Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
Background="{Binding RelativeSource=
{
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}
},
Path=Background
}" CornerRadius="4">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"></TextBlock>
<Image Grid.Row="2" Grid.RowSpan="2" Source="{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}"></Image>
</Grid>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Row="1" Margin="10" Name="lstCategories" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=CategoryName}"></TextBlock>
<Button Grid.Column="1" Padding="2"
Click="cmdView_Clicked" Tag="{Binding}">View ...</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
private void cmdView_Clicked(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
DataRowView row = (DataRowView)cmd.Tag;
lstCategories.SelectedItem = row;
// Alternate selection approach.
//ListBoxItem item = (ListBoxItem)lstCategories.ItemContainerGenerator.ContainerFromItem(row);
//item.IsSelected = true;
MessageBox.Show("You chose category #" + row["CategoryID"].ToString() + ": " + (string)row["CategoryName"]);
}
改变模板
- 使用数据触发器:可以绑定的数据对象中的属性值使用触发器修改模板的属性
- 使用值转换器:实现IValueConverter接口的类,能够将值从绑定的对象转成设置模板中雨格式化相关的属性的值
- 使用模板选择器:模板选择器检查绑定的数据对象,并从几个不同的模板之间进行选择
模板选择器
为不同的项选择不同的模板,需要创建继承自DataTemplateSelector的类。模板选择器的工作方式和前面分析的样式选择器的工作方式相同,他们检查绑定对象并使用提供的逻辑选择合适的模板。
这种方法的缺点是:可能必须创建多个类似的模板。如果模板比较复杂,这种方法会造成大量的重复内容。为了尽量提高可维护性,不应为单个列表创建多个模板,而应使用触发器和样式为模板应用不同的格式。
模板与选择
如果在列表中选择了一项,WPF会自动设置项容器(ListBoxItem对象)的Foreground和Background属性。Foreground属性使用属性继承,所以添加到模板中的任何元素都自动获得新的白色,除非明确指定新的颜色。Background属性不使用属性继承,默认是透明色。
在数据模板中,需要将数据模板的有些属性绑定到ListBoxItem对象上,所以使用Binding.RelativeSource属性从元素树上查找第一个匹配的ListBoxItem对象,一旦找到这个元素,就可以获得她的背景色,并相应的加以使用。
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0" Background="White">
<Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
Background="{Binding RelativeSource=
{
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}
},
Path=Background
}" CornerRadius="4">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"></TextBlock>
</Grid>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
改变项的布局
使用数据模板可以非常灵活的控制项显示的各个方面,但是他们不允许根据项之间的关系更改项的组织方式。不管使用什么样的模板和样式,ListBox控件总是在独立的水平行中放置每个项,并堆叠每行从而创建列表。
可以通过替换列表用于布局其子元素的容器来改变这种布局。为此,使用ItemsPanelTemplate属性。
<ListBox Grid.IsSharedSizeScope="True" Grid.Row="1" Margin="7,3,7,10"
Name="lstProducts" ItemTemplate="{StaticResource ItemTemplate}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" SnapsToDevicePixels="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
注意:大多数列表控件使用VirtualizingStackPanel面板而不是使用标准的StackPanel面板。前者能够高效地处理大量的绑定数据,只创建显示当前可见项所需的元素。后者则创建所有列表元素。
ComboBox控件
和ListBox类一样,ComboBox类也是Selector类的派生类。ConboBox额外增加了两部分:显示当前选择项的选择框和用于选择项的下拉列表。可以通过设置IsDropDownOpen属性打开或者关闭下拉列表。
通常ComboBox只是一个只读的组合框,只可以选择一项,不能随意输入自己的内容。但是,可以通过属性IsReadOnly=false&&IsEditable=true来改变这个行为,选择框就会变成文本框,可以输入文本。
可以通过两种方式在ComboBox控件中放置更复杂的对象。一种是手动添加,可以简单的在StackPanel面板中放置适当的元素,并在ComboBoxItem对象中华封装这个StackPanel面板。还可以通过数据模板将数据对象的内容插入到预先定义好的元素组中。
<ComboBox Margin="5" SnapsToDevicePixels="True" Name="lstProducts" HorizontalContentAlignment="Stretch"
IsEditable="{Binding ElementName=chkIsEditable, Path=IsChecked}"
IsReadOnly="{Binding ElementName=chkIsReadOnly, Path=IsChecked}"
TextSearch.TextPath="{Binding ElementName=txtTextSearchPath, Path=Text}"
>
<ComboBox.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0"></Setter>
<Style.Triggers>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter Property="ComboBoxItem.Background" Value="DarkRed" />
</Trigger>
<Trigger Property="ComboBoxItem.IsHighlighted" Value="True">
<Setter Property="ComboBoxItem.Background" Value="LightSalmon" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0" Background="White">
<Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
Background="{Binding RelativeSource=
{
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ComboBoxItem}
},
Path=Background
}" CornerRadius="4">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"></TextBlock>
<Image Grid.Column="1" Grid.RowSpan="2" Width="50"
Source="{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}"></Image>
</Grid>
</Border>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
WPF进阶技巧和实战03-控件(5-列表、树、网格02)的更多相关文章
- WPF进阶技巧和实战03-控件(3-文本控件及列表控件)
系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...
- WPF进阶技巧和实战03-控件(4-基于范围的控件及日期控件)
系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...
- WPF进阶技巧和实战06-控件模板
逻辑树和可视化树 System.Windows.LogicalTreeHelper System.Windows.Media.VisualTreeHelper 逻辑树类(LogicalTreeHelp ...
- WPF进阶技巧和实战07--自定义元素02
在01节中,研究了如何开发自定义控件,下节开始考虑更特殊的选择:派生自定义面板以及构建自定义绘图 创建自定义面板 创建自定义面板是一种比较常见的自定义控件开发子集,面板可以驻留一个或多个子元素,并且实 ...
- WPF进阶技巧和实战09-事件(2-多点触控)
多点触控输入 多点触控输入和传统的基于比的输入的区别是多点触控识别手势,用户可以移动多根手指以执行常见的操作,放大,旋转,拖动等. 多点触控的输入层次 WPF允许使用键盘和鼠标的高层次输入(例如单击和 ...
- WPF进阶技巧和实战03-控件(1-控件及内容控件)
所有控件都继承自System.Windows.Controls.Control类,这个类添加一些基本结构: 设置控件内容对齐方式 (HorizontalContentAlignment,Vertica ...
- WPF进阶技巧和实战03-控件(5-列表、树、网格03)
数据视图 数据视图是在后台工作的,用于协调绑定数据的集合.使用数据视图可以添加导航逻辑.实现数据过滤.排序.分组. 当将集合或者DataTable绑定到ItemsControl控件时,会不加通告地在后 ...
- WPF进阶技巧和实战03-控件(5-列表、树、网格01)
列表控件 ItemsControl为列表项控件定义了基本功能,下图是ItemsControl的继承关系: 在继承自ItemsControl类的层次结构中,还显示了项封装器(MenuItem.TreeV ...
- WPF进阶技巧和实战08-依赖属性与绑定03
数据提供者 在大多数的代码中,都是通过设置元素的DataContext属性或者列表控件的ItemsSource属性,从而提供顶级的数据源.当数据对象是通过另一个类构造时,可以有其他选择. 一种是作为窗 ...
随机推荐
- [转]C# 互操作性入门系列(四):在C# 中调用COM组件
传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...
- bootstrap 冻结表格,冻结表头
需要的文件下载: bootstrap-table:https://github.com/wenzhixin/bootstrap-table bootstrap-table-fiex-column:ht ...
- WPF/Winform 图表库LiveCharts
早前的博客,自己动手写了几个图表饼状图,柱形图,折线图等,重在思路. 那么如果你不想这么麻烦自己动手,有没有现成的图表库那? 当然有,虽然我也刚开始用没多久,不过还是对这么良心的项目充满了敬佩!! g ...
- C++ 三数之和
来自leecode做题时,发现的双指针用法,觉得挺有意思所以记录一下 链接:https://leetcode-cn.com/problems/3sum 题目: 给你一个包含 n 个整数的数组 nums ...
- 设置一个元素的HTML内容
问题 你需要一个元素中的HTML内容 方法 可以使用Element中的HTML设置方法具体如下: Element div = doc.select("div").first(); ...
- 大数据学习之 LINUX
##大数据学习 古斌6.6 01. linux系统的搭建: 选用 Contos 6.5 x64位系统 (CentOS-6.5-x86_64-minimal.iso) 我选择的为迷你版 模板机: bla ...
- (强制)要求覆写equals必须覆写hashCode(原理分析)
hashCode和equals hashCode和equals用来标识对象,两个方法协同工作可用来判断两个对象是否相等.众所周知,根据生成的哈希将数据散列开来,可以使存取元素更快.对象通过调用Obje ...
- SpringCloud商品服务调用方式之feign
简介:改造电商项目 order-service服务 调用商品服务获取商品信息 Feign: 伪RPC客户端(本质还是用http) 官方文档: https://cloud.spring.io/sprin ...
- Spring Data JPA:解析CriteriaBuilder
源码 在Spring Data JPA相关的文章[地址]中提到了有哪几种方式可以构建Specification的实例,该处需要借助CriteriaBuilder,回顾一下Specification中t ...
- mybatis第一个程序随笔
今天继续学习了解如何写一个mybatis程序 创建了Dao层 1.1 创建一个UserDao接口 1.2 创建UserMapper.xml文件 在mybaits中文手册查找配置信息 <?xml ...