前言
由于最近在进行WPF桌面应用软件开发的相关工作,所以想把从工作当中一些值得积累的东西付诸笔端记录下来跟大家分享也方便自己将来查找。所以博主就又开了一个全新的专栏。。。(保证能更新几篇博文, 不会太烂尾)。
博文配套项目更新地址:https://github.com/mrchipset/simple-wpf.git
这篇博文的内容是如何通过控件模板定义按钮控件的外观。首先给大家找点资料介绍一下WPF,按照博主的惯例还是援引微软官方的介绍:
Windows Presentation Foundation (WPF) 是一个可创建桌面客户端应用程序的 UI 框架。 WPF 开发平台支持广泛的应用开发功能,包括应用模型、资源、控件、图形、布局、数据绑定、文档和安全性。 它是 .NET Framework 的子集,因此,如果你曾经使用 ASP.NET 或 Windows 窗体通过 .NET Framework 构建应用程序,应该会熟悉此编程体验。 WPF 使用 Extensible Application Markup Language (XAML),为应用编程提供声明性模型。
——微软官方文档
从上面的表述当中可以看出,WPF是基于.NET Framework作为基础,使用一种名为XAML的标签声明性语言对UI模型进行定义。所以,理所当然的,本篇博文会将重心放到XAML上,没有一行C#代码实现按钮的外观风格定义。
初识控件模板
首先,我们使用Visual Studio新建一个WPF项目,然后打开自动生成的MainWindow.xaml,在默认的网格布局中添加一个按钮标签,就可以在预览窗口中看到一个Button组件。
<Button Width="120" Height="48" Content="Button"/>
然后通过修改其中的控件模板达到改变按钮外观的目的。接下来我们先实现默认的按钮样式,然后把文字从句中对齐改变为左对齐,再增加与边框的左边距。
<Button Width="120" Height="48" Content="Button">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<TextBlock Text="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10, 0, 0, 0"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
在上述代码中,修改了Button按钮的ControlTemplate,使得按钮的文字编程了左对齐,并设置左边距为10。需要着重介绍的是TemplateBinding关键字,在按钮模板当中,可以把要被自定义风格的父控件的属性绑定到控件模板中的字控件的属性上面。比如上面的Button的Content属性就被绑定到了TextBlock的Text属性上。当然也有例外的时候,我们会在后续博文中遇到。
![]() |
![]() |
触发器
接下来,我们定义模板触发器,实现鼠标进入按钮、点击按钮的动态效果。在刚才的代码中添加如下代码行。实现鼠标进入按钮区域变色和鼠标按下按钮变色。
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#2f96b4"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="red"/>
</Trigger>
</ControlTemplate.Triggers>
需要解释一下的是,Trigger的作用是声明一个触发器,监听控件的特定属性,满足触发条件Value时,就会调用其内部的Setter对目标控件进行特定属性的操作。
比如上面的两个触发器分别监听了按钮的IsMouseOver和IsPressed,鼠标覆盖和按下两个属性,当值为真时修改按钮中边框组件的背景颜色。
资源字典初探
刚才我么已经自定义了一个按钮,实现了按钮的动态效果,其中不涉及1行C#的事件监听代码,是不是发现XAML非常方便。但是试想我们要实现10个这样的按钮,在每个标签里面写这么都内容是不是非常的反智。那么我们接下来介绍一种方法,把这个按钮的风格定义到资源字典当中,可以在需要用到的代码文件中加载对应的资源字典,引用对应的风格定义就可以实现按钮的自定义。
首先需要在VisualStudio中新建一个资源字典项,命名为ButtonStyleDictonary.xaml,在里面对按钮的风格进行定义。可以看出基本就是照搬刚才的代码,只不过从Button中剥离出来了模板部分的代码使用新建的FlatButtonStyle风格的Setter标签对按钮的模板进行设置。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="FlatButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<TextBlock Text="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#2f96b4"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
接下来我们修改主窗口文件,引入资源字典并使用刚刚新建好的风格
<Window x:Class="WpfApp2.MainWindow"
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"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ButtonStyleDictonary.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Style="{StaticResource FlatButtonStyle}" Width="64" Height="28">
Hello
</Button>
</Grid>
</Window>
如果一切正确的话,就可以实现与刚才的代码同样的效果。
自定义外形
刚才我们只是改变了文字的对齐,按钮特效等功能,接下来我们使用Polygon组件改变按钮的外形为填充三角形。
<Style x:Key="ArrowButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Polygon x:Name="border" Fill="{TemplateBinding Background}"
Points="0,0 2,0 1,1" Stroke="Black" StrokeThickness="2"
SnapsToDevicePixels="True"
Stretch="Uniform"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Fill" Value="gray"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Fill" Value="red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
可以发现,我们只是修改了ControlTemplate中的组件,用Polygon代替了Border。同时定义了多边形控件的三个顶点实现一个填充的三角形。
我们在MainWindow.xaml源文件中修改引用的风格名称为ArrowButtonStyle,即可看到三角形箭头按钮。
最后让两个按钮一起出个镜。
总结
我们来总结一下博文的内容
- 通过控件模板修改按钮的外形
- 使用Trigger触发器和Setter动态修改控件的属性
- 使用资源文件和Style标签减少重复的代码工作