Windows Presentation Foundation (WPF) 样式设置和模板化是指一套功能,这套功能使开发者和设计者能够为其产品创建极具视觉表现力的效果和一致的外观。 自定义应用的外观时,需要一个强大的样式设置和模板化模型,以便维护和共享应用内部和应用之间的外观。 WPF 就提供了这样的模型。
WPF 样式设置模型的另一项功能是将呈现与逻辑分离。 设计者可以仅使用 XAML 处理应用外观,与此同时开发者使用 C# 或 Visual Basic 处理编程逻辑。
本文侧重于应用的样式设置和模板化两方面,不讨论任何数据绑定概念。
本文中提供的示例代码基于下图所示的
简单照片浏览应用程序。
此简单照片示例使用样式设置和模板化创建极具视觉表现力的用户体验。 该示例具有两个 TextBlock 元素和一个绑定到图像列表的 ListBox 控件。
可以将 Style 视为一种将一组属性值应用到多个元素的便捷方法。 可以对从 FrameworkElement 或 FrameworkContentElement(如 Window 或 Button)派生的任何元素使用样式。
声明样式的最常见方法是在 XAML 文件的 Resources
部分中声明为资源。 由于样式是一种资源,因此它们同样遵从适用于所有资源的范围规则。 简而言之,声明样式的位置会影响样式的应用范围。 例如,如果在应用定义 XAML 文件的根元素中声明样式,则该样式可以在应用中的任何位置使用。
例如,以下 XAML 代码为 TextBlock
声明了两个样式,一个自动应用于所有 TextBlock
元素,另一个则必须显式引用。
下面举例说明了如何使用上面声明的样式。
My Pictures
Check out my new pictures!
在 WPF 中,控件的 ControlTemplate 用于定义控件的外观。 可以通过定义新的 ControlTemplate 并将其分配给控件来更改控件的结构和外观。 在许多情况下,模板提供了足够的灵活性,从而无需自行编写自定义控件。
每个控件都有一个分配给 Control.Template 属性的默认模板。 该模板将控件的视觉呈现与控件的功能关联起来。 因为在 XAML 中定义了模板,所以无需编写任何代码即可更改控件的外观。 每个模板都是为特定控件(例如 Button)设计的。
通常在 XAML 文件的 Resources
部分中将模板声明为资源。 与其他所有资源一样,范围规则在此也适用。
控件模板比样式复杂得多。 这是因为控件模板重写了整个控件的视觉外观,而样式只是将属性更改应用于现有控件。 但是,控件模板是通过设置 Control.Template 属性来应用的,因此可以使用样式来定义或设置模板。
设计器通常允许创建现有模板的副本并进行修改。 例如,在 Visual Studio WPF 设计器中,选择一个 CheckBox
控件,然后右键单击并选择“编辑模板”>“创建副本”。 此命令会生成一个用于定义模板的样式。
此示例使用 Trigger 设置属性值,但请注意,Trigger 类还有 EnterActions 和 ExitActions 属性,这些属性可使触发器执行操作。
请注意,ListBoxItem 的 MaxHeight 属性设置为 75
。 在下图中,第三项是选中的项。
另一个触发器类型是 EventTrigger,用于根据某个事件的发生启动一组操作。 例如,以下 EventTrigger 对象指定当鼠标指针进入 ListBoxItem 时,MaxHeight 属性在 0.2
秒的时间内动画化为值 90
。 当鼠标离开该项时,属性在 1
秒的时间内返回到原始值。 请注意,不需要为 MouseLeave 动画指定 To 值。 这是因为动画能够跟踪原始值。
在下图中,鼠标指向第三项。
除了 Trigger 和 EventTrigger,还有其他类型的触发器。 MultiTrigger 允许基于多个条件设置属性值。 当条件的属性为数据绑定时,使用 DataTrigger 和 MultiDataTrigger。
控件始终处于特定的状态。 例如,当鼠标在控件的表面上移动时,该控件被视为处于公用状态 MouseOver
。 没有特定状态的控件被视为处于公用 Normal
状态。 状态分为多个组,前面提到的状态属于 CommonStates
状态组。 大多数控件都有两个状态组:CommonStates
和 FocusStates
。 在应用于控件的每个状态组中,控件始终处于每个组的一种状态,例如 CommonStates.MouseOver
和 FocusStates.Unfocused
。 但是,控件不能处于同一组中的两种不同状态,例如 CommonStates.Normal
和 CommonStates.Disabled
。 下面是大多数控件可以识别和使用的状态表。
VisualState 名称 | VisualStateGroup 名称 | 说明 |
---|---|---|
普通 | CommonStates | 默认状态。 |
MouseOver | CommonStates | 鼠标指针悬停在控件上。 |
Pressed | CommonStates | 已按下控件。 |
已禁用 | CommonStates | 已禁用控件。 |
Focused | FocusStates | 控件有焦点。 |
失去焦点 | FocusStates | 控件没有焦点。 |
通过在控件模板的根元素上定义 System.Windows.VisualStateManager,可以在控件进入特定状态时触发动画。 VisualStateManager
声明要监视的 VisualStateGroup 和 VisualState 的组合。 当控件进入受监视状态时,将启动 VisualStateManager
定义的动画。
例如,以下 XAML 代码监视 CommonStates.MouseOver
状态,以对名为 backgroundElement
的元素的填充颜色进行动画处理。 当控件恢复为 CommonStates.Normal
状态时,将还原名为 backgroundElement
的元素的填充颜色。
...
典型的 WPF 应用可能具有多个 UI 资源,它们可应用于整个应用。 这组资源统称为应用的主题。 WPF 支持使用封装为 ResourceDictionary 类的资源字典将 UI 资源打包为主题。
WPF 主题通过使用 WPF 公开的样式设置和模板化机制来定义,该机制用于自定义任何元素的视觉对象。
WPF 主题资源存储在嵌入的资源字典中。 这些资源必须嵌入到已签名的程序集内,并且可以嵌入到与代码本身相同的程序集内或并行程序集内。 对于包含 WPF 控件的程序集 PresentationFramework.dll,主题资源位于一系列并行程序集内。
该主题成为在搜索元素样式时最后查看的位置。 通常,搜索先沿着元素树搜索适当的资源,然后在应用资源集合中查找,最后查询系统。 这样一来,应用开发者便有机会在到达主题之前在树或应用级别上为任何对象重新定义样式。
可以将资源字典定义为单独的文件,这些文件支持跨多个应用重复使用主题。 还可以通过定义多个资源字典来创建可交换的主题,这些资源字典以不同的值提供相同类型的资源。 在应用级别上重新定义这些样式或其他资源是设计应用外观的推荐方法。
若要跨应用共享一组资源(包括样式和模板),可创建 XAML 文件,并定义包含对 shared.xaml
文件的引用的 ResourceDictionary。
它是 shared.xaml
的共享,用于定义包含一组样式和画笔资源的 ResourceDictionary,从而使应用中的控件具有一致的外观。