WPF模板(一)详细介绍
本次随笔来源于电子书,人家的讲解很好,我就不画蛇添足了。
图形用户界面应用程序较之控制台界面应用程序最大的好处就是界面友好、数据显示直观。CUI程序中数据只能以文本的形式线性显示,GUI程序则允许数据以文本、列表、图形等多种形式立体显示。
用户体验在GUI程序设计中起着举足轻重的作用-----用户界面设计成什么样看上去才足够的漂亮?控件如何安排才简单易用并且少犯错误?这些都是设计师需要考虑的问题。WPF系统不但支持传统的Winfrom编程的用户界面和用户体验设计,更支持使用专门的设计工具Blend进行专业设计,同时还推出了以模板为核心的新一代设计理念。
1.1 模板的内涵
从字面上看,模板就是“具有一定规格的样板”,有了它,就可以依照它制造很多一样是实例。我们常把看起来一样的东西称为“一个模子里面刻出来的。”就是这个道理。然而,WPF中的模板的内涵远比这个深刻。
Binding和基于Binding数据驱动UI是WPF的核心部分,WPF最精彩的部分是什么呢?依我看,既不是美轮美奂的3D图形,也不是炫目多彩的动画,而是默默无闻的模板(Template)。实际上,就连2D/3D绘图也常常是为它锦上添花。
Templdate究竟有什么能力能够使得它在WPF体系中获此殊荣呢?这还要从哲学谈起,“形而上者谓之道,形而下者谓之器”,这句话出自《易经》,大意是我们能够观察到的世间万物形象之上的抽象的结果就是思维,而形象之下掩盖的就是其本质。显然,古人已经注意到“形”是连接本质和思维的枢纽,让我们把这句话引入计算机世界。
- “形而上者谓之道”指的就是基于现实世界对万物进行抽象封装,理顺它们之间的关系,这个“道”不就是面向对象思想吗!如果再把面向对象进一步提升、总结出最优的对象组合关系,“道”就上升为设计模式思想。
- “形而下者谓之气”指的是我们能够观察到的世间万物都是物质类容的本质表现形式。“本质与表现”或者说“类容与形式”是哲学范畴内的一对矛盾体。
软件之道并非本书研究的主要类容,本书研究的是WPF。WPF全称Windows Presentation Foundation,Presentation一词的意思就是外观,呈现,表现,也就是说,在WIndows GUI程序这个尺度上,WPF扮演的就是“形”的角色、是程序的外在形式,而程序的内容仍然是由数据和算法构成的业务逻辑。与WPF类似,Winform和Asp.net也都是内容的表现形式。
让我们把尺度缩小到WPF内部。这个系统与程序内容(业务逻辑)的边界是Binding,Binding把数据源源不断从程序内部送出来交由界面元素来显示,又把从界面元素搜集到的数据传回程序内部。界面元素间的沟通则依靠路由事件来完成。有时候路由事件和附加事件也会参与到数据的传输中。让我们思考一个问题:WPF作为Windows的表示方式,它究竟表示的是什么?换句话说,WPF作为一种“形式”,它表现的内容到底是什么?答案是程序的数据和算法----Binding传递的是数据,事件参数携带的也是数据;方法和委托的调用是算法,事件传递消息也是算法----数据在内存里就是一串串字符或字符。算法是一组组看不见摸不着的抽象逻辑,如何恰如其分的把它们展现给用户呢?
加入想表达一个bool类型,同时还想表达用户可以在这两个值之间自由切换这样一个算法,你会怎么做?你一定会想使用一个CheckBox控件来满足要求;再比如颜色值实际上是一串数字,用户基本上不可能只看数字就能想象出真正的颜色,而且用户也不希望只靠输入字符来表示颜色值,这时,颜色值这一“数据内容”的恰当表现形式就是一个填充着真实颜色的色块。,而用户即可以输入值又可以用取色吸管取色来设置值的“算法内容”恰当的表达方式是创建一个ColorPicker控件。相信你已经发现,控件(Control)是数据内容表现形式的双重载体。换句话说,控件即是数据的表现形式让用户可以直观的看到数据,又是算法的表现形式让用户方便的操作逻辑。
作为表现形式,每个控件都是为了实现某种用户操作算法和直观显示某种数据而生,一个控件看上去是什么样子由它的“算法内容”和“数据内容决定”,这就是内容决定形式,这里,我们引入两个概念:
- 控件的算法内容:值控件能展示哪些数据、具有哪些方法、能相应哪些操作、能激发什么事件,简而言之就是控件的功能,它们是一组相关的算法逻辑。
- 控件的数据内容:控件具体展示的数据是什么。
- ControlTemplate:是算法和内容的表现形式,一个控件怎么组织其内部结构才能让它更符合业务逻辑、让用户操作起来更舒服就是由它来控制的。它决定了控件“长成什么样子”,并让程序员有机会在控件原有的内部逻辑基础上扩展自己的逻辑。
- DataTemplate:是数据内容的展示方式,一条数据显示成什么样子,是简单的文本还是直观的图形就由它来决定了。


- <DataTemplate>
- <Grid>
- <StackPanel Orientation="Horizontal">
- <Grid>
- <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding Price}"></Rectangle>
- <TextBlock Text="{Binding Year}"></TextBlock>
- </Grid>
- <TextBlock Text="{Binding Price}" Margin="5,0"></TextBlock>
- </StackPanel>
- </Grid>
- </DataTemplate>
我想,尽管你还没有学习什么DataTempldate,但借助前面学习的基础一样可以看个八九不离十了。
- ContentControl的ContentTempldate属性,相当于给ContentControl的内容穿衣服。
- ItemControl的ItemTemplate,相当于给ItemControl的数据条目穿衣服。
- GridViewColumn的CellTempldate属性,相当于给GridViewColumn的数据条目穿衣服。

- public class Car
- {
- public string AutoMark { get; set; }
- public string Name { get; set; }
- public string Year { get; set; }
- public string TopSpeed { get; set; }
- }
为了在ListBox里面显示Car类型的数据,我们需要准备一个UserControl。命名为CarListItemView。
- <UserControl x:Class="WpfApplication1.CarListViewItem"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
- <Grid Margin="2">
- <StackPanel Orientation="Horizontal">
- <Image x:Name="igLogo" Grid.RowSpan="3" Width="64" Height="64"></Image>
- <StackPanel Margin="5,10">
- <TextBlock x:Name="txtBlockName" FontSize="16" FontWeight="Bold"></TextBlock>
- <TextBlock x:Name="txtBlockYear" FontSize="14"></TextBlock>
- </StackPanel>
- </StackPanel>
- </Grid>
- </UserControl>
- /// <summary>
- /// CarListViewItem.xaml 的交互逻辑
- /// </summary>
- public partial class CarListViewItem : UserControl
- {
- public CarListViewItem()
- {
- InitializeComponent();
- }
- private Car car;
- public Car Car
- {
- get { return car; }
- set
- {
- car = value;
- this.txtBlockName.Text = car.Name;
- this.txtBlockYear.Text = car.Year;
- this.igLogo.Source = new BitmapImage(new Uri(@"Resource/Image/"+car.AutoMark+".png",UriKind.Relative));
- }
- }
- }
类似的原理,我们需要为Car类型准备一个详细信息视图。UserControl名称为CarDetailView,XAML部分代码如下:
- <UserControl x:Class="WpfApplication1.CarDetailView"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
- <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">
- <StackPanel>
- <Image x:Name="imgPhoto" Width="400" Height="250"></Image>
- <StackPanel Orientation="Horizontal" Margin="5,0">
- <TextBlock Text="Name:" FontSize="20" FontWeight="Bold"></TextBlock>
- <TextBlock x:Name="txtBlockName" FontSize="20" Margin="5,0"></TextBlock>
- </StackPanel>
- <StackPanel Orientation="Horizontal" Margin="5,0">
- <TextBlock Text="AutoMark:" FontWeight="Bold"></TextBlock>
- <TextBlock x:Name="txtBlockAutoMark" Margin="5,0"></TextBlock>
- <TextBlock Text="Year:" FontWeight="Bold">
- </TextBlock>
- <TextBlock x:Name="txtBlockYear" Margin="5,0">
- </TextBlock>
- <TextBlock Text="Top Speed:" FontWeight="Bold">
- </TextBlock>
- <TextBlock x:Name="txtTopSpeed" Margin="5,0">
- </TextBlock>
- </StackPanel>
- </StackPanel>
- </Border>
- </UserControl>
后台支持数据大同小异:
- /// <summary>
- /// CarDetailView.xaml 的交互逻辑
- /// </summary>
- public partial class CarDetailView : UserControl
- {
- public CarDetailView()
- {
- InitializeComponent();
- }
- private Car car;
- public Car Car
- {
- get { return car; }
- set
- {
- car = value;
- this.txtBlockName.Text = car.Name;
- this.txtBlockAutoMark.Text = car.AutoMark;
- this.txtBlockYear.Text = car.Year;
- this.txtTopSpeed.Text = car.TopSpeed;
- this.imgPhoto.Source = new BitmapImage(new Uri(@"Resource/Image/" + car.Name + ".jpg", UriKind.Relative));
- }
- }
- }
最后把它们组装到窗体上:
- <Window x:Class="WpfApplication1.Window35"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window35" Height="350" Width="623" xmlns:my="clr-namespace:WpfApplication1">
- <StackPanel Orientation="Horizontal" Margin="5">
- <my:CarDetailView x:Name="carDetailView1" />
- <ListBox x:Name="listBoxCars" Width="180" Margin="5,0" SelectionChanged="listBoxCars_SelectionChanged">
- </ListBox>
- </StackPanel>
- </Window>
窗体的后台代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Shapes;
- namespace WpfApplication1
- {
- /// <summary>
- /// Window35.xaml 的交互逻辑
- /// </summary>
- public partial class Window35 : Window
- {
- public Window35()
- {
- InitializeComponent();
- InitialCarList();
- }
- private void listBoxCars_SelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- CarListViewItem viewItem = e.AddedItems[0] as CarListViewItem;
- if(viewItem!=null)
- {
- carDetailView1.Car = viewItem.Car;
- }
- }
- private void InitialCarList()
- {
- List<Car> infos = new List<Car>() {
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="200", Year="1990"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="250", Year="1998"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="300", Year="2002"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="350", Year="2011"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="500", Year="2020"}
- };
- foreach (Car item in infos)
- {
- CarListViewItem viewItem = new CarListViewItem();
- viewItem.Car = item;
- this.listBoxCars.Items.Add(viewItem);
- }
- }
- }
- public class Car
- {
- public string AutoMark { get; set; }
- public string Name { get; set; }
- public string Year { get; set; }
- public string TopSpeed { get; set; }
- }
- }
运行并单击Item项,运行效果如下图:


- 把Converter以资源字典的形式放进资源字典里(本例使用的方法)。
- 为Converter准备一个静态属性,形成单件模式,在XAML代码里面使用{x:Static}标记扩展来访问。
- //厂商名称转换为Logo路径
- public class AutoMarkToLogoPathConverter:IValueConverter
- {
- /// <summary>
- /// 正向转
- /// </summary>
- /// <param name="value"></param>
- /// <param name="targetType"></param>
- /// <param name="parameter"></param>
- /// <param name="culture"></param>
- /// <returns></returns>
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- return new BitmapImage(new Uri(string.Format(@"Resource/Image/{0}.png",(string)value),UriKind.Relative));
- }
- /// <summary>
- /// 逆向转未用到
- /// </summary>
- /// <param name="value"></param>
- /// <param name="targetType"></param>
- /// <param name="parameter"></param>
- /// <param name="culture"></param>
- /// <returns></returns>
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- throw new NotImplementedException();
- }
- }
- //汽车名称转换为照片路径
- public class NameToPhotoPathConverter:IValueConverter
- {
- /// <summary>
- /// 正向转
- /// </summary>
- /// <param name="value"></param>
- /// <param name="targetType"></param>
- /// <param name="parameter"></param>
- /// <param name="culture"></param>
- /// <returns></returns>
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- return new BitmapImage(new Uri(string.Format(@"Resource/Image/{0}.jpg", (string)value), UriKind.Relative));
- }
- /// <summary>
- /// 逆向转未用到
- /// </summary>
- /// <param name="value"></param>
- /// <param name="targetType"></param>
- /// <param name="parameter"></param>
- /// <param name="culture"></param>
- /// <returns></returns>
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- throw new NotImplementedException();
- }
- }
有了这两个Converter之后我们就可以设计DataTemplate了,完整的XAML代码如下:
- <Window x:Class="WpfApplication1.Window36"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:WpfApplication1.Model"
- Title="Window36" Height="350" Width="623">
- <Window.Resources>
- <!--Converter-->
- <local:AutoMarkToLogoPathConverter x:Key="amp"/>
- <local:NameToPhotoPathConverter x:Key="npp"/>
- <!--DataTemplate For DatialView-->
- <DataTemplate x:Key="DatialViewTemplate">
- <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">
- <StackPanel>
- <Image x:Name="imgPhoto" Width="400" Height="250" Source="{Binding AutoMark,Converter={StaticResource npp}}"></Image>
- <StackPanel Orientation="Horizontal" Margin="5,0">
- <TextBlock Text="Name:" FontSize="20" FontWeight="Bold"></TextBlock>
- <TextBlock FontSize="20" Margin="5,0" Text="{Binding Name}"></TextBlock>
- </StackPanel>
- <StackPanel Orientation="Horizontal" Margin="5,0">
- <TextBlock Text="AutoMark:" FontWeight="Bold"></TextBlock>
- <TextBlock Margin="5,0" Text="{Binding AutoMark}"></TextBlock>
- <TextBlock Text="Year:" FontWeight="Bold">
- </TextBlock>
- <TextBlock Text="{Binding Year}" Margin="5,0">
- </TextBlock>
- <TextBlock Text="Top Speed:" FontWeight="Bold">
- </TextBlock>
- <TextBlock Text="{Binding TopSpeed}" Margin="5,0">
- </TextBlock>
- </StackPanel>
- </StackPanel>
- </Border>
- </DataTemplate>
- <!--Data Template For ItemView-->
- <DataTemplate x:Key="ItemView">
- <Grid Margin="2">
- <StackPanel Orientation="Horizontal">
- <Image x:Name="igLogo" Grid.RowSpan="3" Width="64" Height="64" Source="{Binding Name,Converter={StaticResource amp}}"></Image>
- <StackPanel Margin="5,10">
- <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"></TextBlock>
- <TextBlock Text="{Binding Year}" FontSize="14"></TextBlock>
- </StackPanel>
- </StackPanel>
- </Grid>
- </DataTemplate>
- </Window.Resources>
- <!--窗体内容-->
- <StackPanel Orientation="Horizontal">
- <UserControl ContentTemplate="{StaticResource DatialViewTemplate}" Content="{Binding Path=SelectedItem,ElementName=lbInfos}"></UserControl>
- <ListBox x:Name="lbInfos" ItemTemplate="{StaticResource ItemView}"></ListBox>
- </StackPanel>
- </Window>
代码对于初学者来说有点长但是结构非常简单。其中最重要的有两句:
- /// <summary>
- /// Window36.xaml 的交互逻辑
- /// </summary>
- public partial class Window36 : Window
- {
- public Window36()
- {
- InitializeComponent();
- InitialCarList();
- }
- private void InitialCarList()
- {
- List<Car> infos = new List<Car>() {
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="200", Year="1990"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="250", Year="1998"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="300", Year="2002"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="350", Year="2011"},
- new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="500", Year="2020"}
- };
- this.lbInfos.ItemsSource = infos;
- }
- }
运行程序,效果如下图:

每每提到ControlTemplate我都会想到“披着羊皮的狼”这句话-----披上羊皮之后,虽然看上去像只羊,但其行为仍然是匹狼。狼的行为指的是它能吃别的动物、对着满月嚎叫等事情,控件也有自己的行为,比如显示数据、执行方法、激发事件等。控件的行为要靠编程逻辑来实现,所以也可以把控件的行为称为控件的算法内容。举个例子,WPF中的CheckBox与其基类ToggleButton的功能几乎完全一样,但外观差别上却非常的大,这就是更换ControlTemplate的结果。经过更换ControlTemplate,我们不但可以制作披着CheckBox外衣的ToggleButton,还能制作披着温度计外衣的ProgressBar控件。
- 通过更换ControlTemplate来更换控件的外观,使之具有更优的用户体验和外观。
- 借助ControlTemplate,程序员和设计师可以并行工作,程序员可以使用WPF标准控件进行编程,等设计师的工作完成之后,只需要把新的ControlTemplate应用的程序中即可。
1.3.1 庖丁解牛看控件



- <Style x:Key="RoundCornerTextBoxStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
- <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
- <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
- <Setter Property="BorderThickness" Value="1"/>
- <Setter Property="Padding" Value="1"/>
- <Setter Property="AllowDrop" Value="true"/>
- <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
- <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
- <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type TextBox}">
- <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" CornerRadius="5">
- <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
- </Border>
- <ControlTemplate.Triggers>
- <Trigger Property="IsEnabled" Value="false">
- <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
- </Trigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
这段代码有以下几个看点:
- //
- // button1
- //
- this.button1.Location = new System.Drawing.Point(1100, 199);
- this.button1.Name = "button1";
- this.button1.Size = new System.Drawing.Size(75, 23);
- this.button1.TabIndex = 0;
- this.button1.Text = "报表";
- this.button1.UseVisualStyleBackColor = true;
- this.button1.Click += new System.EventHandler(this.button1_Click);
- //
- // printPreviewDialog1
- //
- this.printPreviewDialog1.AutoScrollMargin = new System.Drawing.Size(0, 0);
- this.printPreviewDialog1.AutoScrollMinSize = new System.Drawing.Size(0, 0);
- this.printPreviewDialog1.ClientSize = new System.Drawing.Size(400, 300);
- this.printPreviewDialog1.Enabled = true;
- this.printPreviewDialog1.Icon = ((System.Drawing.Icon)(resources.GetObject("printPreviewDialog1.Icon")));
- this.printPreviewDialog1.Name = "printPreviewDialog1";
- this.printPreviewDialog1.Visible = false;
同样的逻辑如果在XAML代码里出就变成了这样:
- <Style x:Key="a">
- <Setter Property="pName1" Value="value"></Setter>
- <Setter Property="pName2" Value="value"></Setter>
- <Setter Property="pName3">
- <Setter.Value>
- <!--ObjectValue-->
- </Setter.Value>
- </Setter>
- <Setter Property="pName4">
- <Setter.Value>
- <!--ObjectValue-->
- </Setter.Value>
- </Setter>
- lt;/Style>
使用Style是,如过Value值比较简单,那么就直接用Attribute值表示,如果Value值不能用一个简单的字符串来表示那么就需要用XAML的属性对象语法。例子中,TextBox的Template是一个ControlTemplate对象,如此复杂的值只能使用属性对象语法来描述。对于Style,后面会有专门的章节来描述。
- <Window
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
- x:Class="WPFApplication.MainWindow"
- x:Name="Window"
- Title="MainWindow"
- Width="385" Height="275">
- <Window.Background>
- <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
- <GradientStop Color="#FF4D91C6" Offset="0"/>
- <GradientStop Color="#FFD9DBF1" Offset="1"/>
- </LinearGradientBrush>
- </Window.Background>
- <Grid x:Name="LayoutRoot">
- <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Margin="57,49,0,0" Width="255.487" Style="{DynamicResource RoundCornerTextBoxStyle}"/>
- <TextBox TextWrapping="Wrap" Text="TextBox" Margin="57,106,56.513,109.163" d:LayoutOverrides="Height" Style="{DynamicResource RoundCornerTextBoxStyle}"/>
- <Button Content="Button" VerticalAlignment="Bottom" Margin="149,0,145,42.163"/>
- </Grid>
- </Window>
程序运行效果如下图:


- <Grid x:Name="LayoutRoot" Margin="5">
- <ListBox>
- <TextBlock>Darren</TextBlock>
- <TextBlock>Andy</TextBlock>
- <TextBlock>Jacky</TextBlock>
- <TextBlock>T-Soft</TextBlock>
- </ListBox>
- </Grid>
如果我们把代码改成这样:
- <Grid x:Name="LayoutRoot" Margin="5">
- <ListBox>
- <!--ItemsPanel-->
- <ListBox.ItemsPanel>
- <ItemsPanelTemplate>
- <StackPanel Orientation="Horizontal"></StackPanel>
- </ItemsPanelTemplate>
- </ListBox.ItemsPanel>
- <!--条目-->
- <TextBlock>Darren</TextBlock>
- <TextBlock>Andy</TextBlock>
- <TextBlock>Jacky</TextBlock>
- <TextBlock>T-Soft</TextBlock>
- </ListBox>
- </Grid>
条目就会包装在一个水平排列的StackPanel中,从而横向排列,如下图所示:




- <DataTemplate>
- <Grid>
- <StackPanel Orientation="Horizontal">
- <Grid>
- <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding Price}"></Rectangle>
- <TextBlock Text="{Binding Year}"></TextBlock>
- </Grid>
- <TextBlock Text="{Binding Price}" Margin="5,0"></TextBlock>
- </StackPanel>
- </Grid>
- </DataTemplate>
- <Style BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
- <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
- <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
- <Setter Property="BorderThickness" Value="1"/>
- <Setter Property="Padding" Value="1"/>
- <Setter Property="AllowDrop" Value="true"/>
- <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
- <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
- <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type TextBox}">
- <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" CornerRadius="5">
- <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
- </Border>
- <ControlTemplate.Triggers>
- <Trigger Property="IsEnabled" Value="false">
- <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
- </Trigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- lt;/Style>
Style没有X:key标记,默认为引用到所有的x:type指定的控件上,如果不想应用则将style标记为{x:null}。运行效果如下图:

- <Window
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:WPFApplication"
- xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
- x:Class="WPFApplication.Window3"
- x:Name="Window"
- Title="Window3"
- Width="288" Height="181">
- <Window.Resources>
- <!--DataTemplate-->
- <DataTemplate DataType="{x:Type local:Unit}">
- <Grid>
- <StackPanel Orientation="Horizontal">
- <Grid>
- <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding Price}"></Rectangle>
- <TextBlock Text="{Binding Year}"></TextBlock>
- </Grid>
- <TextBlock Text="{Binding Year}" Margin="5,0"></TextBlock>
- </StackPanel>
- </Grid>
- </DataTemplate>
- <!--数据源-->
- <c:ArrayList x:Key="ds">
- <local:Unit Year="2001年" Price="100"></local:Unit>
- <local:Unit Year="2002年" Price="120"></local:Unit>
- <local:Unit Year="2003年" Price="140"></local:Unit>
- <local:Unit Year="2004年" Price="160"></local:Unit>
- <local:Unit Year="2005年" Price="180"></local:Unit>
- <local:Unit Year="2006年" Price="200"></local:Unit>
- </c:ArrayList>
- </Window.Resources>
- <StackPanel>
- <ListBox ItemsSource="{StaticResource ds}"></ListBox>
- <ComboBox ItemsSource="{StaticResource ds}" Margin="5"></ComboBox>
- </StackPanel>
- </Window>
代码中的DataTemplate的目标数据类型和ListBox的条目类型都是Unit:
- public class Unit
- {
- public string Year{get;set;}
- public int Price{get;set;}
- }
此时DataTemplate会自动加载到所有的Unit类型对象上,尽管我没有为ListBox和CompBox指定ItemTemplate,一样会得到下图的效果:

- <Window
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- x:Class="WPFApplication.Window4"
- x:Name="Window"
- Title="Window4"
- Width="314" Height="210">
- <Window.Resources>
- <!--DataTemplate-->
- <DataTemplate DataType="Unit">
- <Grid>
- <StackPanel Orientation="Horizontal">
- <Grid>
- <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding XPath=@Price}"></Rectangle>
- <TextBlock Text="{Binding XPath=@Year}"></TextBlock>
- </Grid>
- <TextBlock Text="{Binding XPath=@Year}" Margin="5,0"></TextBlock>
- </StackPanel>
- </Grid>
- </DataTemplate>
- <!--数据源-->
- <XmlDataProvider x:Key="ds" XPath="Units/Unit">
- <x:XData>
- <Units xmlns="">
- <Unit Price="100" Year="2001"></Unit>
- <Unit Price="120" Year="2002"></Unit>
- <Unit Price="140" Year="2003"></Unit>
- <Unit Price="160" Year="2004"></Unit>
- <Unit Price="180" Year="2005"></Unit>
- <Unit Price="200" Year="2006"></Unit>
- </Units>
- </x:XData>
- </XmlDataProvider>
- </Window.Resources>
- <StackPanel>
- <ListBox ItemsSource="{Binding Source={StaticResource ds}}"></ListBox>
- <ComboBox ItemsSource="{Binding Source={StaticResource ds}}" Margin="5"></ComboBox>
- </StackPanel>
- </Window>
XML的优势就是可以方便的表示带有层级的数据,比如:年级----班级----小组 或 主菜单---次菜单----三级菜单。同时WPF准备了TreeView和MenuItem控件来显示层级数据。能够帮助层级控件显示层级数据的模板是HierachicalDataTemplate。下面两个实际工作中常见的例子:
- <?xml version="1.0" encoding="utf-8" ?>
- <Data xmlns="">
- <Grade Name="一年级">
- <Class Name="甲班">
- <Group Name="A组">
- </Group>
- <Group Name="B组">
- </Group>
- <Group Name="C组">
- </Group>
- </Class>
- <Class Name="乙班">
- <Group Name="A组">
- </Group>
- <Group Name="B组">
- </Group>
- <Group Name="C组">
- </Group>
- </Class>
- </Grade>
- <Grade Name="二年级">
- <Class Name="甲班">
- <Group Name="A组">
- </Group>
- <Group Name="B组">
- </Group>
- <Group Name="C组">
- </Group>
- </Class>
- <Class Name="乙班">
- <Group Name="A组">
- </Group>
- <Group Name="B组">
- </Group>
- <Group Name="C组">
- </Group>
- </Class>
- </Grade>
- </Data>
程序XAML代码如下:
- <Window x:Class="WPFApplication.Window6"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window6" Height="268" Width="362">
- <Window.Resources>
- <!--数据源-->
- <XmlDataProvider x:Key="ds" Source="XMLStudent.xml" XPath="Data/Grade"></XmlDataProvider>
- <!--年级模板-->
- <HierarchicalDataTemplate DataType="Grade" ItemsSource="{Binding XPath=Class}">
- <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
- </HierarchicalDataTemplate>
- <!--班级模板-->
- <HierarchicalDataTemplate DataType="Class" ItemsSource="{Binding XPath=Group}">
- <RadioButton Content="{Binding XPath=@Name}" GroupName="gn"></RadioButton>
- </HierarchicalDataTemplate>
- <!--小组模板-->
- <HierarchicalDataTemplate DataType="Group" ItemsSource="{Binding XPath=Student}">
- <CheckBox Content="{Binding XPath=@Name}"></CheckBox>
- </HierarchicalDataTemplate>
- </Window.Resources>
- <Grid>
- <TreeView Margin="5" ItemsSource="{Binding Source={StaticResource ds}}">
- </TreeView>
- </Grid>
- </Window>
程序运行效果如下图:

- <?xml version="1.0" encoding="utf-8" ?>
- <Data xmlns="">
- <Operation Name="文件" Gesture="F">
- <Operation Name="新建" Gesture="N">
- <Operation Name="项目" Gesture="Ctr+P"/>
- <Operation Name="网站" Gesture="Ctr+W"/>
- <Operation Name="文档" Gesture="Ctr+D"/>
- </Operation>
- <Operation Name="保存" Gesture="S"/>
- <Operation Name="打印" Gesture="P"/>
- <Operation Name="退出" Gesture="X"/>
- </Operation>
- <Operation Name="编辑" Gesture="E">
- <Operation Name="剪切" Gesture="Ctr+X"/>
- <Operation Name="复制" Gesture="Ctr+C"/>
- <Operation Name="粘贴" Gesture="Ctr+V"/>
- </Operation>
- </Data>
程序XAML代码如下:
- <Window x:Class="WPFApplication.Window7"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window7" Height="300" Width="300">
- <Window.Resources>
- <!--数据源-->
- <XmlDataProvider x:Key="ds" Source="MenuXML.xml" XPath="Data/Operation"></XmlDataProvider>
- <!--Operation模板-->
- <HierarchicalDataTemplate DataType="Operation" ItemsSource="{Binding XPath=Operation}" >
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="{Binding XPath=@Name}" Margin="10,0"></TextBlock>
- <TextBlock Text="{Binding XPath=@Gesture}"></TextBlock>
- </StackPanel>
- </HierarchicalDataTemplate>
- </Window.Resources>
- <StackPanel>
- <Menu ItemsSource="{Binding Source={StaticResource ds}}"></Menu>
- </StackPanel>
- </Window>
运行效果如下图:

- <StackPanel MenuItem.Click="StackPanel_Click">
- <Menu ItemsSource="{Binding Source={StaticResource ds}}"></Menu>
- </StackPanel>
事件处理代码如下:
- private void StackPanel_Click(object sender, RoutedEventArgs e)
- {
- MenuItem item = e.OriginalSource as MenuItem;
- XmlElement xe = item.Header as XmlElement;
- MessageBox.Show(xe.Attributes["Name"].Value);
- }
我们先来寻找由ControlTemplate生成的控件。首先设计一个ControlTemplate并把它应用在一个UserControl控件上。界面上还有一个Button,在它的Click事件处理器中我们检索ControlTemplate生成的代码。
- <Window x:Class="WPFApplication.Window8"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window8" Height="300" Width="300">
- <Window.Resources>
- <ControlTemplate x:Key="xtTemp">
- <StackPanel Background="Orange">
- <TextBox Margin="6" x:Name="textbox1"></TextBox>
- <TextBox Margin="6,0" x:Name="textbox2"></TextBox>
- <TextBox Margin="6" x:Name="textbox3"></TextBox>
- </StackPanel>
- </ControlTemplate>
- </Window.Resources>
- <StackPanel Background="Yellow">
- <UserControl x:Name="uc" Template="{StaticResource xtTemp}" Margin="5"></UserControl>
- <Button Content="Find By Name" Width="120" Height="30" Click="Button_Click"></Button>
- </StackPanel>
- </Window>
Button的事件处理器代码如下:
- private void Button_Click(object sender, RoutedEventArgs e)
- {
- TextBox tb = this.uc.Template.FindName("textbox1", this.uc) as TextBox;
- tb.Text = "Hello WPF";
- StackPanel sp = tb.Parent as StackPanel;
- (sp.Children[1] as TextBox).Text = "Hello ControlTemplate";
- (sp.Children[2] as TextBox).Text = "I Can Find YOU.";
- }
运行效果如下:


- public class Student38
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string Skill { get; set; }
- public bool HasJob { get; set; }
- }
界面XAML代码如下:
- <Window x:Class="WpfApplication1.Window38"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:WpfApplication1"
- Title="Window38" Height="227" Width="269">
- <Window.Resources>
- <!--数据对象-->
- <local:Student38 x:Key="stu" Id="1" Skill="WPF" Name="Timothy" HasJob="True"></local:Student38>
- <!--DataTemplate-->
- <DataTemplate x:Key="dtStu">
- <Border BorderBrush="Orange" BorderThickness="2" CornerRadius="5">
- <StackPanel>
- <TextBlock Text="{Binding Id}" Margin="5"></TextBlock>
- <TextBlock x:Name="txtBlockName" Text="{Binding Name}" Margin="5"></TextBlock>
- <TextBlock Text="{Binding Skill}" Margin="5"></TextBlock>
- </StackPanel>
- </Border>
- </DataTemplate>
- </Window.Resources>
- <!--主窗体布局-->
- <StackPanel>
- <ContentPresenter x:Name="cp" Content="{StaticResource stu}" ContentTemplate="{StaticResource dtStu}" Margin="5">
- </ContentPresenter>
- <Button Content="Find" Margin="5,0" Click="Button_Click">
- </Button>
- </StackPanel>
- </Window>
Button的事件处理器代码如下:
- private void Button_Click(object sender, RoutedEventArgs e)
- {
- TextBlock tb = this.cp.ContentTemplate.FindName("txtBlockName", this.cp) as TextBlock;
- MessageBox.Show(tb.Text);
- //Student38 stu = this.cp.Content as Student38;
- //MessageBox.Show(stu.Name);
- }
未被注释的代码是使用DataTemplate的FindName方法获取由DataTemplate生成的控件并访问其属性,被注释的代码是直接使用其底层数据。显然,为了获取Student的某个属性,应该使用被注释的代码而不必要绕到控件上来,除非你想得到的是控件的长度,高度。与业务逻辑无关的纯UI属性。
- public class Student39
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string Skill { get; set; }
- public bool HasJob { get; set; }
- }
准备数据集合,呈现数据的工作全部由XAML代码来完成:
- <Window x:Class="WpfApplication1.Window39"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
- xmlns:local="clr-namespace:WpfApplication1"
- Title="Window39" Height="338" Width="446">
- <Window.Resources>
- <!--数据集合-->
- <c:ArrayList x:Key="stuList">
- <local:Student39 Id="1" Name="Timoty Liu" Skill="WPF" HasJob="True"></local:Student39>
- <local:Student39 Id="2" Name="Tom Chang" Skill="BI/SQL" HasJob="True"></local:Student39>
- <local:Student39 Id="3" Name="Guan Chong" Skill="Writing" HasJob="False"></local:Student39>
- <local:Student39 Id="4" Name="Shanshan" Skill="C#/Java" HasJob="False"></local:Student39>
- <local:Student39 Id="5" Name="Pingping Zhang" Skill="Writing" HasJob="False"></local:Student39>
- <local:Student39 Id="6" Name="kenny Tian" Skill="Asp.net" HasJob="False"></local:Student39>
- </c:ArrayList>
- <!--DataTemplate-->
- <DataTemplate x:Key="nameDT">
- <TextBox x:Name="txtBoxName" Text="{Binding Name}"></TextBox>
- </DataTemplate>
- <DataTemplate x:Key="skillDT">
- <TextBox x:Name="txtSkill" Text="{Binding Skill}"></TextBox>
- </DataTemplate>
- <DataTemplate x:Key="hasJobDT">
- <CheckBox IsChecked="{Binding HasJob}"></CheckBox>
- </DataTemplate>
- </Window.Resources>
- <Grid Margin="5">
- <ListView x:Name="lvStudent" ItemsSource="{StaticResource stuList}">
- <ListView.View>
- <GridView>
- <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}"></GridViewColumn>
- <GridViewColumn Header="姓名" CellTemplate="{StaticResource nameDT}"></GridViewColumn>
- <GridViewColumn Header="技术" CellTemplate="{StaticResource skillDT}"></GridViewColumn>
- <GridViewColumn Header="已工作" CellTemplate="{StaticResource hasJobDT}"></GridViewColumn>
- </GridView>
- </ListView.View>
- </ListView>
- </Grid>
- </Window>
程序运行效果如下图:

- <DataTemplate x:Key="nameDT">
- <TextBox x:Name="txtBoxName" Text="{Binding Name}" GotFocus="txtBoxName_GotFocus"></TextBox>
- </DataTemplate>
因为我们是在DataTemplate里面添加了事件处理器,所以界面上任何一个由此DataTemplate生成的TextBox都会在获得焦点的时候调用txtBoxName_GotFocus这个事件处理器。txtBoxName_GotFocus的代码如下:
- private void txtBoxName_GotFocus(object sender, RoutedEventArgs e)
- {
- TextBox tb = e.OriginalSource as TextBox; //获取事件发起的源头
- ContentPresenter cp = tb.TemplatedParent as ContentPresenter;//获取模板目标
- Student39 stu = cp.Content as Student39;//获取业务逻辑数据
- this.lvStudent.SelectedItem = stu;//设置ListView选中项
- //访问界面元素
- ListViewItem lvi = this.lvStudent.ItemContainerGenerator.ContainerFromItem(stu) as ListViewItem;
- CheckBox cb = this.FindVisualChild<CheckBox>(lvi);
- MessageBox.Show(cb.Name);
- }
- private ChildType FindVisualChild<ChildType>(DependencyObject obj) where ChildType : DependencyObject
- {
- for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
- {
- DependencyObject child = VisualTreeHelper.GetChild(obj,i);
- if (child != null && child is ChildType)
- {
- return child as ChildType;
- }
- else
- {
- ChildType childOfChild = FindVisualChild<ChildType>(child);
- if(childOfChild!=null)
- {
- return childOfChild;
- }
- }
- }
- return null;
- }
当使用GridView作为ListView的View属性时,如果某一列使用TextBox作为CellTemplate,那么即使这列中的TextBox被鼠标单击并获得了焦点ListView也不会把此项做为自己的SelectedItem。所以,txtBoxName_GotFocus的前半部分是获得数据的源头(TextBox),然后沿UI元素树上朔到DataTemplate目标控件(ContentPresenter)并获取它的内容,它的内容一定是一个Student实例。
运行程序,并单击某个显示姓名的TextBox,效果如下图所示:

- <Window x:Class="WpfApplication1.Window40"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window40" Height="310" Width="426">
- <Window.Resources>
- <Style TargetType="TextBlock">
- <Setter Property="FontSize" Value="24"></Setter>
- <Setter Property="TextDecorations" Value="Underline"></Setter>
- <Setter Property="FontStyle" Value="Italic"></Setter>
- </Style>
- </Window.Resources>
- <StackPanel Margin="5">
- <TextBlock Text="Hello WPF!"></TextBlock>
- <TextBlock Text="This is a sample for style!"></TextBlock>
- <TextBlock Text="by Time 2012-11-12!" Style="{x:Null}"></TextBlock>
- </StackPanel>
- </Window>
因为Style的内容属性是Setters,所以我们可以直接在<Style>标签的内容区域使用Setter。

- <Window x:Class="WpfApplication1.Window41"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window41" Height="258" Width="352">
- <Window.Resources>
- <Style TargetType="CheckBox">
- <Style.Triggers>
- <Trigger Property="IsChecked" Value="True">
- <Trigger.Setters>
- <Setter Property="FontSize" Value="20"></Setter>
- <Setter Property="Foreground" Value="Orange"></Setter>
- </Trigger.Setters>
- </Trigger>
- </Style.Triggers>
- </Style>
- </Window.Resources>
- <Window.Background>
- <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
- <GradientStop Color="#FF4589D8" Offset="0" />
- <GradientStop Color="White" Offset="1" />
- </LinearGradientBrush>
- </Window.Background>
- <StackPanel>
- <CheckBox Content="锄禾日当午" Margin="5"></CheckBox>
- <CheckBox Content="汗滴禾下土" Margin="5,0"></CheckBox>
- <CheckBox Content="谁知盘中餐" Margin="5"></CheckBox>
- <CheckBox Content="粒粒皆辛苦" Margin="5,0"></CheckBox>
- </StackPanel>
- </Window>
因为Triggers不是Style的内容属性,所以<Style.Trigger>...</Style.Trigger>这层标签不能省略,但Trigger的Setters属性是Trigger的内容属性,所以<Trigger.Setters>...</Trigger.Setters>这层标签是可以省略的。

- <Style TargetType="CheckBox">
- <Style.Triggers>
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="IsChecked" Value="True"></Condition>
- <Condition Property="Content" Value="粒粒皆辛苦"></Condition>
- </MultiTrigger.Conditions>
- <MultiTrigger.Setters>
- <Setter Property="FontSize" Value="20"></Setter>
- <Setter Property="Foreground" Value="Orange"></Setter>
- </MultiTrigger.Setters>
- </MultiTrigger>
- </Style.Triggers>
- </Style>
运行效果如下图:

- <Window x:Class="WpfApplication1.Window42"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:WpfApplication1"
- Title="Window42" Height="184" Width="324">
- <Window.Resources>
- <local:L2BConverter x:Key="cbtr"></local:L2BConverter>
- <Style TargetType="TextBox">
- <Style.Triggers>
- <DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self},Path=Text.Length,Converter={StaticResource cbtr}}" Value="false">
- <Setter Property="BorderBrush" Value="Red"></Setter>
- <Setter Property="BorderThickness" Value="1"></Setter>
- </DataTrigger>
- </Style.Triggers>
- </Style>
- </Window.Resources>
- <StackPanel>
- <TextBox Margin="5"></TextBox>
- <TextBox Margin="5,0"></TextBox>
- <TextBox Margin="5"></TextBox>
- </StackPanel>
- </Window>
这个例子中唯一需要解释的就是DataTrigger的Binding。为了将控件自身做为数据源,我们使用了RelativeSource,初学者经常认为“不明确指出Source的值Binding就会将自己作为数据的来源”,这是错误的,因为不明确指出Source的值Binding就会把控件的DataContext做为自己的数据来源。Binding的Path设置为Text.Length,即我们关注的是字符串的长度。长度是一个具体的数字,如何基于这个长度值来做判断呢?这就用到了Converter。我们创建如下Converter:
- public class L2BConverter : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- int textLength = (int)value;
- return textLength > 6 ? true : false;
- }
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- throw new NotImplementedException();
- }
- }
经过Converter转换以后,长度值就会变为bool类型值。DataTrigger的value设置为false,也就是说当TextBox的文本长度小于7时DataTrigger会使用自己一组Setter把TextBox的边框设置为红色。运行效果如下图:

- <Window x:Class="WpfApplication1.Window43"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window43" Height="262" Width="425">
- <Window.Resources>
- <Style TargetType="ListBoxItem">
- <!--使用Style设置Datatemplate-->
- <Setter Property="ContentTemplate">
- <Setter.Value>
- <DataTemplate>
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="{Binding Id}" Width="60"></TextBlock>
- <TextBlock Text="{Binding Name}" Width="120"></TextBlock>
- <TextBlock Text="{Binding Skill}" Width="60"></TextBlock>
- </StackPanel>
- </DataTemplate>
- </Setter.Value>
- </Setter>
- <!--MultiDataTrigger-->
- <Style.Triggers>
- <MultiDataTrigger>
- <MultiDataTrigger.Conditions>
- <Condition Binding="{Binding Path=Id}" Value="2"></Condition>
- <Condition Binding="{Binding Path=Name}" Value="Darren"></Condition>
- </MultiDataTrigger.Conditions>
- <MultiDataTrigger.Setters>
- <Setter Property="Background" Value="Orange"></Setter>
- </MultiDataTrigger.Setters>
- </MultiDataTrigger>
- </Style.Triggers>
- </Style>
- </Window.Resources>
- <StackPanel>
- <ListBox x:Name="lbInfos" Margin="5"></ListBox>
- </StackPanel>
- </Window>
后台代码如下:
- public Window43()
- {
- InitializeComponent();
- InitialInfo();
- }
- private void InitialInfo()
- {
- List<Student38> infos = new List<Student38>() {
- new Student38(){ Id=2, Name="Darren", Skill="WPF"},
- new Student38(){ Id=1, Name="Tom", Skill="Java"},
- new Student38(){ Id=3, Name="Jacky", Skill="Asp.net"},
- new Student38(){ Id=2, Name="Andy", Skill="C#"},
- };
- this.lbInfos.ItemsSource = infos;
- }
Student38类已经在上面的文章中提到,再此就不再多讲。运行效果如下图:

- <Window x:Class="WpfApplication1.Window44"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window44" Height="258" Width="377">
- <Window.Resources>
- <Style TargetType="Button">
- <Style.Triggers>
- <!--鼠标进入-->
- <EventTrigger RoutedEvent="MouseEnter">
- <BeginStoryboard>
- <Storyboard>
- <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Width"></DoubleAnimation>
- <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Height"></DoubleAnimation>
- </Storyboard>
- </BeginStoryboard>
- </EventTrigger>
- <!--鼠标离开-->
- <EventTrigger RoutedEvent="MouseLeave">
- <BeginStoryboard>
- <Storyboard>
- <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Width"></DoubleAnimation>
- <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height"></DoubleAnimation>
- </Storyboard>
- </BeginStoryboard>
- </EventTrigger>
- </Style.Triggers>
- </Style>
- </Window.Resources>
- <Grid>
- <Button Width="40" Height="40" Content="OK"></Button>
- </Grid>
- </Window>
无需任何c#代码,我们就获得了如下图所示的结果:


WPF模板(一)详细介绍的更多相关文章
- WPF的数据绑定详细介绍
数据绑定:是应用程序 UI 与业务逻辑之间建立连接的过程. 如果绑定正确设置并且数据提供正确通知,则当数据的值发生更改时,绑定到数据的视觉元素会自动反映更改. 数据绑定可能还意味着如果视觉元素中数据的 ...
- thinkPHP 模板中的语法知识 详细介绍(十二)
原文:thinkPHP 模板中的语法知识 详细介绍(十二) 本章节:介绍模板中的语法,详细的语法介绍 一.导入CSS和JS文件 ==>记住常量的是大写 1.css link .js sc ...
- ThinkPHP框架视图详细介绍 View 视图--模板(九)
原文:ThinkPHP框架视图详细介绍 View 视图--模板(九) 视图也是ThinkPHP使用的核心部分: 一.模板的使用 a.规则 模板文件夹下[TPL]/[分组文件夹/][模板主题文件夹/]和 ...
- doT.js详细介绍
doT.js详细介绍 doT.js特点是快,小,无依赖其他插件. 官网:http://olado.github.iodoT.js详细使用介绍 使用方法:{{= }} for interpolati ...
- WPF快速入门系列(7)——深入解析WPF模板
一.引言 模板从字面意思理解是“具有一定规格的样板".在现实生活中,砖块都是方方正正的,那是因为制作砖块的模板是方方正正的,如果我们使模板为圆形的话,则制作出来的砖块就是圆形的,此时我们并不 ...
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- C++中引用与指针的区别(详细介绍)
C++中引用与指针的区别(详细介绍) C++中的引用与指针的区别 指向不同类型的指针的区别在于指针类型可以知道编译器解释某个特定地址(指针指向的地址)中的内存内容及大小,而void*指针则只表示一 ...
- Java 集合系列 05 Vector详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- WDCP是什么 关于WDCP的详细介绍
WDCP是WDlinux Control Panel的简称,是一套用PHP开发的Linux服务器管理系统以及虚拟主机管理系统,,旨在易于使用Linux系统做为我们的网站服务器,以及平时对Linux服务 ...
- Thinkphp框架拓展包使用方式详细介绍--验证码实例(十一)
原文:Thinkphp框架拓展包使用方式详细介绍--验证码实例(十一) 拓展压缩包的使用方式详细介绍 1:将拓展包解压:ThinkPHP3.1.2_Extend.zip --> 将其下的 \ ...
随机推荐
- SqlServer中循环和条件语句
if语句使用示例 declare @a int set @a=12 if @a>100 begin ...
- [angularjs] angularjs系列笔记(二)指令
重复HTML元素 ng-repeat指令可以重复HTML元素 <body> <div ng-app="Home" ng-controller="inde ...
- 49.Linux-wpa_cli使用之WIFI开启,扫描热点,连接热点,断开热点,WIFI关闭(49)
本章学习内容: 1.WIFI如何开启 2.扫描热点 3.连接热点 4. 断开热点 5.关闭WIFI 本节使用的是wpa_supplicant工具,它主要包含wpa_supplicant(命令行模式)与 ...
- java过滤器(简化认证)
最近在看过滤器,刚刚实现了过滤器的简化认证功能: 使用过滤器简化认证: 在Web应用程序中,过滤器的一个关键用例是保护应用程序不被未授权的用户访问.为跨国部件公司开发的客户支持应用程序使用了一种非常原 ...
- bash array
bash 仅支持一维数组. 而且数组下标是从0开始的为数组赋值:array=(1 4 7 2 5 8) #以空格为分割符,()为数组str="this is test string& ...
- eclipse编写js代码没有提示
安装插件 点击Help,选择Eclipse Marketplace... 搜索js,安装AngularJS Eclipse 重启eclipse,右键项目,选择Configure(配置),选择Conve ...
- 5.枚举和注解_EJ
第30条: 用enum代替int常量 枚举类型是指由一组固定的常量组成合法值得类型.例如一年中的季节,太阳系中的行星或一副牌中的花色.在开发中我们经常在类使用static final来定义一个int常 ...
- javascript中call()、apply()的区别
call().apply()的区别: 相同点: 1.call()和apply()都可以用来间接调用函数,都可以显式调用所需的this.即,锚点滑动任何函数可以作为任何对象的方法来调用. 2.两个方法都 ...
- wx-charts 微信小程序图表 -- radarChart C# .net .ashx 测试
radarChart:原始代码 new wxCharts({ canvasId: 'radarCanvas', type: 'radar', categories: ['1', '2', '3', ' ...
- HTML笔记(适合新手入门)
HTML Web 标准构成 Web标准不是某一个标准,而是由W3C和其他标准化组织制定的一系列标准的集合. 主要包括结构(Structure).表现(Presentation)和行为(Behavior ...