首先,我们先利用Blend解开TextBox控件的内部结构。利用Blend新建一个WPF项目,在界面上添加一个TextBox。
我们看到,TextBox的四个角都是直角,我们现在试着把他的角变成圆角:
选中这个控件,右键->编辑模板->编辑副本。这里面我们选择编辑副本是在当前这个TextBox的模板之上进行修改。然后弹出如下图所示的对话框。其中名称指的是编辑后的Style的键值,如果选择全部应用所有的TextBox将都会设置该Style。定义位置指的是该Style代码存放的位置,可以是App级别的资源文件中、可以是当前的文档中,也可以是专门存放资源的自定义文档。
点击确定后,整个TextBox的结构展现在我们前面:
<Style x:Key="TextBoxStyle1" 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}">
<Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Microsoft_Windows_Themes:ListBoxChrome>
<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>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true"
CornerRadius="10">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
运行结果如下:
通过上面的例子,可以总结如下:
1、ControlTemplate设置往往放到一个Style中,对应的属性是控件的Template属性,直接用ControlTemplate为Template赋值。
2、一个控件实际上由不同的子控件构成,这就是控件内部的VisualTree,VisualTree实际上就构成了整个控件的ControlTemplate的框架。另外,我们把在界面上可见的控件元素构成的节点树叫做LogicTree,需要和VisualTree进行区分。
3、从代码中可以看到很多的TemplateBinding,以BorderBrush="{TemplateBinding BorderBrush}"为例,我们设置的Border的BorderBrush通过TemplateBinding绑定了目标控件(TextBox)的BorderBrush,这样一旦TextBox的BorderBrush有任何的更新,都会及时的通知到Border并进行更新。
在这里,有一点需要说明一下,我们只看到TemplateBinding的Path是BorderBrush,却没有设置TemplateBinding的数据源Source,Binding是怎么找到目标控件的BorderBrush的呢,其实{TemplateBinding BorderBrush}就等同于{Binding BorderBrush RelativeSource={ RelativeSource TemplateParent}}。
二、ItemsControl的PanelTemplate
ItemsControl具有一个名为ItemsPanel的属性,他的数据类型是ItemsPanelTemplate。ItemsPanelTemplate也是一种控件的Template,它的作用是让我们有机会控制ItemsControl的条目容器。
下面我们将ListBox的Item由纵向排列改成横向排列:
首先,新建一个项目增加如下代码:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="WpfApplication7.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<ListBox Margin="80,24,72,26">
<TextBlock Text="张三"/>
<TextBlock Text="李四"/>
<TextBlock Text="王五"/>
</ListBox>
</Grid>
</Window>
运行:
我们修改ListBox的ItemsPanel,达到横向排列ListBox的Items的目的:
<Grid x:Name="LayoutRoot">
<ListBox Margin="80,24,72,26">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<TextBlock Text="张三"/>
<TextBlock Text="李四"/>
<TextBlock Text="王五"/>
</ListBox>
</Grid>
运行效果: