跳到主要内容

5.1 资源字典与样式

学习目标​

你将把颜色、间距和控件样式从零散的属性中提取出来,并能根据资源是否需要运行时替换选择引用方式。前提:已有 WpfBasics WPF 项目,知道 XAML 和基础布局。

资源不是 CSS​

后端 Web 页面常用 CSS 类统一外观;WPF 的资源字典存放实际对象,例如 Brush、Style、DataTemplate 或字符串。资源通常从当前元素向祖先、应用等范围查找。窗口资源可以覆盖应用中同名的资源,因此同一个键在不同窗口未必指向同一对象。不要把所有资源放进某个页面,再期待其他窗口也能访问它们。

Style 为指定控件类型批量提供 setter 和 trigger。控件上直接写的本地属性值通常比样式 setter 优先,所以样式写了蓝色、按钮却仍是红色时,先检查按钮自己的 Background。完整的依赖属性优先级见 依赖属性与路由事件。

引用方式适用场景
StaticResource加载时解析资源,适合固定样式、模板与大多数资源
DynamicResource保存资源引用表达式,资源键指向的对象替换后可重新求值,适合主题颜色

动态引用有额外开销,并且主要用于依赖属性;不是任何 CLR 属性都能使用。静态引用不意味着对象不可修改:修改共享 Brush 对象仍可能影响使用者。主题切换常采用替换字典或同键资源,此时动态引用才能持续跟随新对象。静态资源要注意声明顺序,不应依赖未定义的前向引用。

创建并合并资源字典​

在项目中新建目录 Resources,添加以下完整文件。WPF SDK 项目会把常规资源字典 XAML 作为 Page 编译;若手动改变了 Build Action,需要恢复为 Page。

Resources/Theme.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="AccentBrush" Color="#2563EB" />
<SolidColorBrush x:Key="AccentTextBrush" Color="White" />

<Style x:Key="PrimaryButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Padding" Value="16,8" />
<Setter Property="Background" Value="{DynamicResource AccentBrush}" />
<Setter Property="Foreground" Value="{DynamicResource AccentTextBrush}" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>

<Style TargetType="{x:Type TextBox}">
<Setter Property="Padding" Value="8" />
<Setter Property="MinWidth" Value="180" />
</Style>
</ResourceDictionary>

在已有 App.xaml 的 Application.Resources 中放入以下片段;保留原来的类名和启动属性。合并字典中键冲突时,后面的字典优先;主字典直接定义的同键资源优先于合并字典。

App.xaml:Application.Resources 内容
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Theme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

窗口内使用显式按钮样式;TextBox 则自动获得按类型注册的隐式样式。隐式 Style 省略 x:Key,其键由 TargetType 决定,不是按名称匹配所有看起来相似的控件。

MainWindow.xaml:Window 内的内容
<StackPanel Margin="24">
<TextBox Text="主题演示" />
<Button Margin="0,12,0,0" Content="主要操作"
Style="{StaticResource PrimaryButtonStyle}" />
</StackPanel>

可在主窗口已显示后,例如现有按钮事件处理器中,执行这行代码演示替换资源。片段需要 using System.Windows; 和 using System.Windows.Media;;运行在 UI 线程。

MainWindow.xaml.cs:事件处理器内片段
Application.Current.Resources["AccentBrush"] =
new SolidColorBrush(Colors.DarkGreen);

如果想在现有样式上增加危险操作颜色,使用 BasedOn="{StaticResource PrimaryButtonStyle}" 创建另一个样式,并遵守目标类型兼容要求。不要把每种颜色复制成一套结构相同的模板。

常见错误​

资源路径相对于包含该引用的文件或项目打包方式解析,移动字典后要一起检查路径。显式样式不会因为 TargetType 相同就自动应用;必须引用其键。默认控件模板可能在悬停、按下时改变外观,单个 Background setter 不保证所有状态都使用该色,完整外观定制应阅读 模板。

练习与验收​

给页面中的两个按钮都应用 PrimaryButtonStyle,再执行资源替换。验收:两个按钮在普通状态都显示新色,TextBox 自动使用统一 Padding;只在其中一个按钮本地设置红色后,它按本地值显示。解释这分别来自共享资源、动态引用和属性优先级。

官方资料​