8天入门wpf—— 第四天 模板

今天说下wpf中的模板,前面一篇中我们讲到了style,但是style所能做的仅仅是在现有控件的基础上进行修修补补,但是如果我们想

彻底颠覆控件样式,那么我们就必须使用这一篇所说的模板。

老外写书都喜欢在篇头搞一个类图,方便我们宏观认识,这里我也上一个。


一:控件模板

1:ControlTemplate

我们知道wpf的控件都是继承自Control,在Control类中有一个Template属性,类型就是ControlTemplate。


那么利用这个ControlTemplate就可以彻底的颠覆控件的默认外观,这里我把一个checkbox变成一个小矩形,蛮有意思的。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ControlTemplate x:Key="rect" TargetType="{x:Type CheckBox}">
 <StackPanel>
 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
 <Rectangle.Fill>
 <SolidColorBrush Color="White"/>
 </Rectangle.Fill>
 </Rectangle>
 </StackPanel>
 </ControlTemplate>
 </Window.Resources>
 <Canvas>
 <CheckBox Template="{StaticResource ResourceKey=rect}" Content="我是CheckBox"/>
 </Canvas>
</Window>


确实,我们干了一件漂亮的事情,把checkbox变成了“小矩形”,但是我们发现了一个小问题,为什么我的Content=“xxx”没有显示到模板上?

很简单,我们已经重定义了控件模板,默认模板将会被覆盖...

2:ContentPresenter

幸好,wpf给我们提供了一个ContentPresenter,它的作用就是把原有模板的属性原封不动的投放到自定义模板中。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ControlTemplate x:Key="rect" TargetType="{x:Type CheckBox}">
 <StackPanel>
 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
 <Rectangle.Fill>
 <SolidColorBrush Color="White"/>
 </Rectangle.Fill>
 </Rectangle>
 <ContentPresenter/>
 </StackPanel>
 </ControlTemplate>
 </Window.Resources>
 <Canvas>
 <CheckBox Template="{StaticResource ResourceKey=rect}" Content="我是CheckBox"/>
 </Canvas>
</Window>


当然你也可以玩一些小技巧,比如我想在"矩形“和”文字”中间设置边距,那么我们可以设置ContentPresenter的margin。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ControlTemplate x:Key="rect" TargetType="{x:Type CheckBox}">
 <StackPanel>
 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
 <Rectangle.Fill>
 <SolidColorBrush Color="White"/>
 </Rectangle.Fill>
 </Rectangle>
 <ContentPresenter Margin="10" />
 </StackPanel>
 </ControlTemplate>
 </Window.Resources>
 <Canvas>
 <CheckBox Template="{StaticResource ResourceKey=rect}" Content="我是CheckBox"/>
 </Canvas>
</Window>


如果你够聪明,你会发现我设置的margin是一个非常呆板的事情,意思就是说能不能根据具体控件灵活控制margin呢?答案肯定是没问题的,

因为我们记得一个控件可以绑定到另一个控件上,比如这里我将模板中的Margin绑定到原控件中的Padding上去。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ControlTemplate x:Key="rect" TargetType="{x:Type CheckBox}">
 <StackPanel>
 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
 <Rectangle.Fill>
 <SolidColorBrush Color="White"/>
 </Rectangle.Fill>
 </Rectangle>
 <ContentPresenter Margin="{TemplateBinding Padding}" />
 </StackPanel>
 </ControlTemplate>
 </Window.Resources>
 <Canvas>
 <CheckBox Template="{StaticResource ResourceKey=rect}" Content="我是CheckBox" Padding="10"/>
 </Canvas>
</Window>


3:Trigger

我们知道style里面也是有trigger的,废话不多说,上代码说话。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ControlTemplate x:Key="rect" TargetType="{x:Type CheckBox}">
 <ControlTemplate.Resources>
 <SolidColorBrush x:Key="redBrush" Color="Red"/>
 </ControlTemplate.Resources>
 <StackPanel>
 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
 <Rectangle.Fill>
 <SolidColorBrush Color="White"/>
 </Rectangle.Fill>
 </Rectangle>
 <ContentPresenter/>
 </StackPanel>
 <ControlTemplate.Triggers>
 <Trigger Property="IsChecked" Value="True">
 <Setter TargetName="breakRectangle" Property="Fill" Value="{StaticResource ResourceKey=redBrush}">
 </Setter>
 </Trigger>
 </ControlTemplate.Triggers>
 </ControlTemplate>
 </Window.Resources>
 <Canvas>
 <CheckBox Template="{StaticResource ResourceKey=rect}" Content="我是CheckBox"/>
 </Canvas>
</Window>


最后形成的效果就是当checkbox选中时为实心框,不选中为空心框。

4:与Style混搭

可能刚才我也说了,style只能在原有的控件基础上修修补补,如果我们让Style修补Control控件的Template属性时,此时我们是不是

就可以实现ControlTemplate和Style的混搭呢?

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <Style x:Key="cbx" TargetType="{x:Type CheckBox}">
 <Setter Property="Template">
 <Setter.Value>
 <ControlTemplate TargetType="{x:Type CheckBox}">
 <ControlTemplate.Resources>
 <SolidColorBrush x:Key="redBrush" Color="Red"/>
 </ControlTemplate.Resources>
 <StackPanel>
 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
 <Rectangle.Fill>
 <SolidColorBrush Color="White"/>
 </Rectangle.Fill>
 </Rectangle>
 <ContentPresenter/>
 </StackPanel>
 <ControlTemplate.Triggers>
 <Trigger Property="IsChecked" Value="True">
 <Setter TargetName="breakRectangle" Property="Fill" Value="{StaticResource ResourceKey=redBrush}">
 </Setter>
 </Trigger>
 </ControlTemplate.Triggers>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
 </Style>

 </Window.Resources>
 <Canvas>
 <CheckBox Style="{StaticResource ResourceKey=cbx}" Content="我是CheckBox"/>
 </Canvas>
</Window>


二:数据模板

现在我们已经知道“控件模板”是用于改变控件外观,那么“数据模板”顾名思义就是控制数据的显示方式,下面做个demo让person绑定到listbox上。

namespace WpfApplication1
{
 /// <summary>
 /// MainWindow.xaml 的交互逻辑
 /// </summary>
 public partial class MainWindow : Window
 {
 public static string name = "一线码农";

 public MainWindow()
 {
 InitializeComponent();
 }
 }

 public class PersonList : ObservableCollection<Person>
 {
 public PersonList()
 {
 this.Add(new Person() { Name = "一线码农", Age = 24, Address = "上海" });
 this.Add(new Person() { Name = "小师妹", Age = 20, Address = "上海" });
 }
 }

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

 public int Age { get; set; }

 public string Address { get; set; }
 }
}

xaml:

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 xmlns:src="clr-namespace:WpfApplication1"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ObjectDataProvider x:Key="personList" ObjectType="{x:Type src:PersonList}"/>
 </Window.Resources>
 <Grid>
 <ListBox ItemsSource="{Binding Source={StaticResource ResourceKey=personList}}"></ListBox>
 </Grid>
</Window>


最后我们发现,listbox中并没有呈现我们需要的数据,只是呈现了当前类的ToString()方法,很简单,因为我们绑定的不是简单的数据类型集合,

而是多字段的复杂类型,更重要的是我们并没有告诉wpf该如何呈现person数据。

<1>重写Tostring()

既然wpf在Render数据的时候呈现的是当前的ToString()形式,那下面我们来重写ToString()试试看。

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

 public int Age { get; set; }

 public string Address { get; set; }

 public override string ToString()
 {
 return string.Format("姓名:{0}, 年龄:{1}, 地址:{2}", Name, Age, Address);
 }
 }

最后看看效果,如我们所愿,person信息已经呈现。


<2>DataTemplate重写

或许有的人比较苛刻,他需要person是作为矩形一块一块的呈现,而不是这些简单的形式,那么此时我们就可以用DataTemplate来颠覆。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 xmlns:src="clr-namespace:WpfApplication1"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ObjectDataProvider x:Key="personList" ObjectType="{x:Type src:PersonList}"/>
 <DataTemplate x:Key="rect">
 <Border Name="border" BorderBrush="Aqua" BorderThickness="1" Padding="5" Margin="5">
 <StackPanel>
 <StackPanel Orientation="Horizontal">
 <TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
 <TextBlock Text="{Binding Age}" Margin="5,0,0,0"/>
 </StackPanel>
 <StackPanel Orientation="Horizontal">
 <TextBlock Text="{Binding Address}" Margin="5,0,0,0"/>
 </StackPanel>
 </StackPanel>
 </Border>
 </DataTemplate>
 </Window.Resources>
 <Grid>
 <ListBox ItemsSource="{Binding Source={StaticResource ResourceKey=personList}}"
 ItemTemplate="{StaticResource ResourceKey=rect}"></ListBox>
 </Grid>
</Window>


哈哈,果然是以一块一块的形式展现,大功告成,当然这里的”触发器“和”style混搭“跟ConrolTemplate非常相似,我想应该不需要累赘了。

三: ItemsPanelTemplate

在条目控件(ItemControl)里面,有一个属性叫ItemPanel,类型是ItemPanelTemplate。


那么ItemsPanelTemplate主要用来干什么的呢?首先我们要知道常见的条目控件有:ListBox,Menu,StatusBar,比如拿ListBox来说,

我们经过仔细研究,发现ItemBox的ItemPanel其实是一个VisualizingStackPanel,就是说ListBox的每一项的排列方式是遵循StackPanel的

原则,也就是从上到下的排列方式,比如”一线码农“和”小师妹“是按照竖行排列方式,好,我现在的要求就是能够”横排“,该如何做到呢?

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 xmlns:src="clr-namespace:WpfApplication1"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <ObjectDataProvider x:Key="personList" ObjectType="{x:Type src:PersonList}"/>
 <DataTemplate x:Key="rect">
 <Border Name="border" BorderBrush="Aqua" BorderThickness="1" Padding="5" Margin="5">
 <StackPanel>
 <StackPanel Orientation="Horizontal">
 <TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
 <TextBlock Text="{Binding Age}" Margin="5,0,0,0"/>
 </StackPanel>
 <StackPanel Orientation="Horizontal">
 <TextBlock Text="{Binding Address}" Margin="5,0,0,0"/>
 </StackPanel>
 </StackPanel>
 </Border>
 </DataTemplate>
 <ItemsPanelTemplate x:Key="items">
 <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"/>
 </ItemsPanelTemplate>
 </Window.Resources>
 <Grid>
 <ListBox ItemsSource="{Binding Source={StaticResource ResourceKey=personList}}"
 ItemTemplate="{StaticResource ResourceKey=rect}" ItemsPanel="{StaticResource ResourceKey=items}"></ListBox>
 </Grid>
</Window>


哈哈,确实有意思,我们改变了ListBox中Item的默认排序方向,当然在menu,statusBar中我们也可以用同样的方式来更改。

四: HierarchicalDataTemplate

它是针对具有分层数据结构的控件设计的,比如说TreeView,相当于可以每一个层级上做DataTemplate,很好很强大。

<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 xmlns:src="clr-namespace:WpfApplication1"
 Title="MainWindow" Height="350" Width="525">
 <Window.Resources>
 <XmlDataProvider x:Key="Info" XPath="Nations">
 <x:XData>
 <Nations xmlns="">
 <Nation Name="中国">
 <Provinces>
 <Province Name="安徽">
 <Citys>
 <City Name="安庆">
 <Countrys>
 <Country Name="潜山"/>
 <Country Name="桐城"/>
 </Countrys>
 </City>
 <City Name="合肥">
 <Countrys>
 <Country Name="长丰"/>
 <Country Name="肥东"/>
 </Countrys>
 </City>
 </Citys>
 </Province>
 <Province Name="江苏">
 <Citys>
 <City Name="南京">
 <Countys>
 <Country Name="溧水"/>
 <Country Name="高淳"/>
 </Countys>
 </City>
 <City Name="苏州">
 <Countys>
 <Country Name="常熟"/>
 </Countys>
 </City>
 </Citys>
 </Province>
 </Provinces>
 </Nation>
 </Nations>
 </x:XData>
 </XmlDataProvider>
 <HierarchicalDataTemplate DataType="Nation" ItemsSource="{Binding XPath=Provinces/Province}">
 <StackPanel Background="AliceBlue">
 <TextBlock FontSize="20" Text="{Binding XPath=@Name}"/>
 </StackPanel>
 </HierarchicalDataTemplate>
 <HierarchicalDataTemplate DataType="Province" ItemsSource="{Binding XPath=Citys/City}">
 <StackPanel Background="LightBlue">
 <TextBlock FontSize="18" Text="{Binding XPath=@Name}"/>
 </StackPanel>
 </HierarchicalDataTemplate>
 <HierarchicalDataTemplate DataType="City" ItemsSource="{Binding XPath=Countrys/Country}">
 <StackPanel Background="LightBlue">
 <TextBlock FontSize="18" Text="{Binding XPath=@Name}"/>
 </StackPanel>
 </HierarchicalDataTemplate>
 <HierarchicalDataTemplate DataType="Country">
 <StackPanel Background="LightSalmon">
 <TextBlock FontSize="18" Text="{Binding XPath=@Name}"/>
 </StackPanel>
 </HierarchicalDataTemplate>
 </Window.Resources>
 <TreeView ItemsSource="{Binding Source={StaticResource ResourceKey=Info},XPath=Nation}"></TreeView>
</Window>

时间: 2024-09-17 08:50:04

8天入门wpf—— 第四天 模板的相关文章

入门教程:JSP标准模板库(上)

js|标准|教程|模板|入门教程 简介JSP标准模板库(JSTL)是SUN公司发布的一个针对JSP开发的新组件.JSTL允许你使用标签(tags)来进行JSP页面开发,而不是使用多数JSP程序员已经习惯了的scriptlet代码方式开发.JSTL几乎能够做到传统JSP scriptlet代码能做的任何事情.你可能会疑惑,为什么我们需要另一种这样的HTML生成语言呢? STL允许JSP程序员使用tags而不是JAVA代码来编程.为了展示为什么这个是更优越的,下面将给出示例.我们会检查一个非常简单的

MySQL入门学习(四)

mysql MySQL入门学习(四) --学习篇   上篇我们学会了如何创建一个数据库和数据库表,并知道如何向数据库表中添加记录.   那么我们如何从数据库表中检索数据呢? 1.从数据库表中检索信息 实际上,前面我们已经用到了SELECT语句,它用来从数据库表中检索信息. select语句格式一般为: SELECT 检索关键词 FROM 被检索的表 WHERE 检索条件(可选) 以前所使用的" * "表示选择所有的列. 下面继续使用我们在上篇文章中创建的表mytable: 2.查询所有

Windows 8风格应用开发入门 二十四 App Bar构建

构建应用栏的目的的显示导航.命令和始终隐藏不需要的使用的工具.我们可以把应用栏放在页面 顶部或底部或同时存在顶部和底部. 默认情况在AppBar是隐藏的,当用户单击右键.按下Win+Z .或从屏幕的顶部或底部边缘轻松时可显示或关闭AppBar.当然我们也可以通过编程的方式将AppBar设 置为当用户做选择或与应用交互时显示. 构建AppBar基本步骤 通常我们构建一个应用的 AppBar,只需要三步就可以完成: 开发入门 二十四 App Bar构建-jenkins构建自由风格"> 如何构建

Android开发入门(十四)显示图像 14.2 ImageSwitcher

前面的一节,介绍了如何组合使用Gallery和ImageView.但是,有的时候当你在gallery中点击一个图片 ,你可能不希望一个图片"突然地"在imageview中显示出来.例如,你可能希望给某个图片设置一些切换动 画.此时,就需要使用ImageSwitcher和Gallery一起使用.下面展示如何使用ImageSwitcher. 1. 创 建一个工程,ImageSwitcher. 2. main.xml中的代码. <?xml version="1.0"

Android开发入门(十四)显示图像 14.1 Gallery和ImageView

Gallery可以显示一系列的图片,并且可以横向滑动.下面展示如何使用Gallery去显示一系列的图片. 1. 创建一个工程,Gallery. 2. main.xml中的代码. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_

Android入门之Activity四种启动模式(standard、singleTop、singleTask、singleInstance)_Android

当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中.Activity启动模式的设置在AndroidManifest.xml文件中,通过配置Activity的属性android:launchMode=""设置. 一.启动模式介绍 启动模式简单地说就是Activity启动时的策略,在AndroidManifest.xml中的标签的android:launchMode属性设置: 启动模式有4种,分别为standard.singleTop.s

UWP开发入门(十四)—— UserControl中Adaptive UI的小技巧

原文:UWP开发入门(十四)-- UserControl中Adaptive UI的小技巧 本篇我们通过绘制一个非常简单的UserControl控件,来分享一下对Adaptive UI的理解及一些图形绘制的技巧. 现在流行的APP都少不了精致的用户头像,首先假设我们需要绘制如下的图形作为默认头像: <UserControl x:Class="AdaptiveUserControl.Circle0" xmlns="http://schemas.microsoft.com/w

Docker入门教程(四)Docker Registry

本文讲的是Docker入门教程(四)Docker Registry,[编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第四篇,介绍了Docker Registry,它是Docker中的重要组件.本文通过情景演绎的方式对其进行了介绍,图文并茂,强烈推荐读者阅读. 在Docker系列教程的上一篇文章中,我们讨论了Dockerfile的重要性并提供了一系列Dockerfile的命令,使镜像的自动构建更加容易.在这篇文章中,我们将介绍Docker的一个重要组件

Manifest.xml 入门基础 (四) &amp;lt;application&amp;gt;标签

Manifest.xml 入门基础 (四) <application>标签 一个AndroidManifest.xml中必须含有一个Application标签,这个标签声明了每一个应用程序的组件及其属性(如icon,label,permission等) <application android:allowClearUserData=["true" | "false"] android:allowTaskReparenting=["true