在WPF中,每个可视化控件都有一个默认的模板,通过替换这个默认的模板,可以实现自定义的控件外观和行为。例如:ButtonTemplate 是自定义模板,经过button1.Template = ButtonTemplate以后button1的外观就是自定义外观,button1的行为也由ButtonTemplate决定。一个自定义模板由3个部分组成:资源、控件外观、触发器。
我们通过一个例子来学习自定义控件。做成以后的图片:

以下代码就是一个自定义模板的基本框架,虽然什么也不能做,但是可以编译通过了。
<Window.Resources>
<!-- Button模板 -->
<ControlTemplatex:Key="ButtonTemplate"TargetType="Button">
<!--资源 -->
<ControlTemplate.Resources>
</ControlTemplate.Resources>
<!--控件外观-->
<!--触发器 -->
<ControlTemplate.Triggers>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<ButtonName="buttonDemo"Template="{StaticResource ButtonTemplate}"></Button>
我们来增加一点点东西。
<Button Name="buttonDemo"
Template="{StaticResource ButtonTemplate}"
Margin="12,12,12,12"
Content ="Hello World"
Width="180"
Height ="72"
Foreground ="Black"
FontSize="24"
FontStyle="Italic"
FontWeight="Bold"
HorizontalContentAlignment ="Center"
VerticalContentAlignment ="Center">
</Button>
然后开始修改自定义模板:Template="{StaticResource ButtonTemplate}"
Margin="12,12,12,12"
Content ="Hello World"
Width="180"
Height ="72"
Foreground ="Black"
FontSize="24"
FontStyle="Italic"
FontWeight="Bold"
HorizontalContentAlignment ="Center"
VerticalContentAlignment ="Center">
</Button>
<Window.Resources>
<!-- Button模板 -->
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
<!-- 资源 -->
<ControlTemplate.Resources>
<!-- Button表面的渐变 -->
<LinearGradientBrush x:Key="ButtonShapeGradientBrush" StartPoint ="0,0" EndPoint ="0,1">
<GradientStop Color="#FF555555" Offset="0.0" />
<GradientStop Color="#FF999999" Offset="0.2" />
<GradientStop Color="#FFEEEEEE" Offset="0.5" />
<GradientStop Color="#FF999999" Offset="0.8" />
<GradientStop Color="#FF555555" Offset="1.0" />
</LinearGradientBrush>
<!-- 鼠标单击的动画 -->
<Storyboard x:Key="MouseClickStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="buttonShape" Storyboard.TargetProperty="RadiusX" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime ="00:00:00" Value="40" />
<SplineDoubleKeyFrame KeyTime ="00:00:00.50" Value="64" />
<SplineDoubleKeyFrame KeyTime ="00:00:01" Value="40" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="buttonShape" Storyboard.TargetProperty="RadiusY" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime ="00:00:00" Value="40" />
<SplineDoubleKeyFrame KeyTime ="00:00:00.50" Value="64" />
<SplineDoubleKeyFrame KeyTime ="00:00:01" Value="40" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<!-- 控件外观 -->
<Grid>
<Rectangle Name="buttonShape"
Width="{TemplateBinding Button.Width}"
Height="{TemplateBinding Button.Height}"
RadiusX ="40"
RadiusY ="40"
Stroke="Black"
StrokeThickness="2"
Fill="{StaticResource ButtonShapeGradientBrush}">
</Rectangle>
<ContentPresenter Name="content"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
TextElement.Foreground ="{TemplateBinding Foreground}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontStyle="{TemplateBinding FontStyle}"
TextElement.FontWeight ="{TemplateBinding FontWeight}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
</ContentPresenter>
</Grid>
<!-- 触发器 -->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="content" Property="TextElement.Foreground" Value="#FFFF0000" />
<Setter TargetName="buttonShape" Property="Stroke" Value="#FFFF0000" />
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="buttonShape">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="content">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Button Name="buttonDemo"
Template="{StaticResource ButtonTemplate}"
Margin="12,12,12,12"
Content ="Hello World"
Width="180"
Height ="72"
Foreground ="Black"
FontSize="24"
FontStyle="Italic"
FontWeight="Bold"
HorizontalContentAlignment ="Center"
VerticalContentAlignment ="Center">
</Button>
代码简单,没什么好说的啊!<!-- Button模板 -->
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
<!-- 资源 -->
<ControlTemplate.Resources>
<!-- Button表面的渐变 -->
<LinearGradientBrush x:Key="ButtonShapeGradientBrush" StartPoint ="0,0" EndPoint ="0,1">
<GradientStop Color="#FF555555" Offset="0.0" />
<GradientStop Color="#FF999999" Offset="0.2" />
<GradientStop Color="#FFEEEEEE" Offset="0.5" />
<GradientStop Color="#FF999999" Offset="0.8" />
<GradientStop Color="#FF555555" Offset="1.0" />
</LinearGradientBrush>
<!-- 鼠标单击的动画 -->
<Storyboard x:Key="MouseClickStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="buttonShape" Storyboard.TargetProperty="RadiusX" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime ="00:00:00" Value="40" />
<SplineDoubleKeyFrame KeyTime ="00:00:00.50" Value="64" />
<SplineDoubleKeyFrame KeyTime ="00:00:01" Value="40" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="buttonShape" Storyboard.TargetProperty="RadiusY" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime ="00:00:00" Value="40" />
<SplineDoubleKeyFrame KeyTime ="00:00:00.50" Value="64" />
<SplineDoubleKeyFrame KeyTime ="00:00:01" Value="40" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<!-- 控件外观 -->
<Grid>
<Rectangle Name="buttonShape"
Width="{TemplateBinding Button.Width}"
Height="{TemplateBinding Button.Height}"
RadiusX ="40"
RadiusY ="40"
Stroke="Black"
StrokeThickness="2"
Fill="{StaticResource ButtonShapeGradientBrush}">
</Rectangle>
<ContentPresenter Name="content"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
TextElement.Foreground ="{TemplateBinding Foreground}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontStyle="{TemplateBinding FontStyle}"
TextElement.FontWeight ="{TemplateBinding FontWeight}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
</ContentPresenter>
</Grid>
<!-- 触发器 -->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="content" Property="TextElement.Foreground" Value="#FFFF0000" />
<Setter TargetName="buttonShape" Property="Stroke" Value="#FFFF0000" />
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="buttonShape">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="content">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Button Name="buttonDemo"
Template="{StaticResource ButtonTemplate}"
Margin="12,12,12,12"
Content ="Hello World"
Width="180"
Height ="72"
Foreground ="Black"
FontSize="24"
FontStyle="Italic"
FontWeight="Bold"
HorizontalContentAlignment ="Center"
VerticalContentAlignment ="Center">
</Button>