WPF 如何设计出好看的样式?¶
- 归档日期:2026-08-25
- 问题:如果 WPF 想设计出好看的样式,应该怎么做?
- 技术口径:.NET 10 WPF,默认采用 Windows Fluent Design 方向。
一句话结论¶
先使用 .NET 10 内置的 WPF Fluent Theme,再建立一套小而完整的设计系统:语义颜色、字体层级、4 倍数间距、圆角、图标和控件状态。不要从零重画所有控件,也不要混用多个主题库。
界面是否好看,通常只有一小部分取决于按钮模板;更重要的是:
- 信息层级是否清晰。
- 间距和对齐是否一致。
- 字体与颜色是否克制。
- 所有控件状态是否完整。
- 页面在不同窗口大小和 DPI 下是否仍然自然。
- 亮色、暗色和高对比度是否可用。
一、先选择视觉路线¶
路线 A:官方 WPF Fluent Theme,默认推荐¶
.NET 9+ 已内置 Windows 11 风格的 WPF Fluent Theme,支持亮色、暗色和系统主题;.NET 10 又继续改进了 Fluent 样式。
最简单的应用方式:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ThemeMode="System">
<Application.Resources>
<!-- 这里只合并自己的设计令牌和应用样式 -->
</Application.Resources>
</Application>
也可以显式合并 Fluent 资源:
<ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
不要同时用 ThemeMode 和手动 Fluent 字典做同一件事,否则资源覆盖关系容易混乱。当前 ThemeMode API 在 .NET 10 文档中仍带有 WPF0001 实验性标记;如果项目禁止实验性 API,可以采用显式资源字典并自行管理主题切换。
推荐安装或运行微软的 WPF Gallery。它不仅展示控件,还包含颜色、排版、图标、布局和仪表盘示例,可以直接查看对应 XAML。
路线 B:第三方 Fluent 库¶
如果内置主题缺少产品需要的 Navigation、Snackbar、ContentDialog、自定义标题栏、Mica 等组件,可以评估 WPF UI(lepoco/wpfui)。它采用 Fluent 路线并提供额外导航和沉浸式控件。
在采用前验证:
- .NET 10 和目标 Windows 版本。
- 所需 DataGrid、对话框、导航和标题栏能力。
- Designer、Hot Reload、高 DPI 和高对比度。
- 许可证、版本升级成本和维护活跃度。
路线 C:Material Design¶
如果产品已经明确采用 Google Material Design,而不是 Windows Fluent,可以使用 MaterialDesignInXamlToolkit。它包含常用控件样式、Cards、Dialogs、调色板和图标。
不要在同一个应用中混用 Fluent、Material 和 Metro 控件风格。字体、圆角、阴影、动画和控件状态来自不同体系时,界面通常会显得拼凑。
二、建立设计令牌,而不是到处写十六进制颜色¶
设计令牌是界面中的基础变量。控件只引用“用途”,不直接写死颜色和尺寸。
推荐目录:
Themes/
├─ Tokens.Colors.xaml
├─ Tokens.Typography.xaml
├─ Tokens.Spacing.xaml
├─ Theme.Light.xaml
├─ Theme.Dark.xaml
├─ Controls.Button.xaml
├─ Controls.Input.xaml
├─ Controls.DataGrid.xaml
├─ Controls.Navigation.xaml
└─ Components.xaml
1. 语义颜色¶
不要命名为 Blue500Brush、Gray100Brush,优先根据用途命名:
<SolidColorBrush x:Key="AppSurfaceBrush" Color="#FFF9F9F9" />
<SolidColorBrush x:Key="AppSurfaceSecondaryBrush" Color="#FFF2F2F2" />
<SolidColorBrush x:Key="AppTextPrimaryBrush" Color="#E4000000" />
<SolidColorBrush x:Key="AppTextSecondaryBrush" Color="#9E000000" />
<SolidColorBrush x:Key="AppBorderBrush" Color="#24000000" />
<SolidColorBrush x:Key="AppAccentBrush" Color="#FF0067C0" />
<SolidColorBrush x:Key="AppDangerBrush" Color="#FFC42B1C" />
<SolidColorBrush x:Key="AppSuccessBrush" Color="#FF107C10" />
暗色主题定义相同的 Key,但换成适合暗色的值。控件通过 DynamicResource 引用:
规则:
- 需要运行时切换主题的颜色使用
DynamicResource。 - 不会变化的尺寸、模板和结构优先使用
StaticResource。 - 强调色只用于主要操作、选中和焦点,不要让整页到处都是品牌色。
- 错误不能只用红色表达,还应配合图标和文字。
- 亮色和暗色不是简单把 RGB 取反,应分别调节表面层级、边框和文本透明度。
2. 间距令牌¶
采用 4 epx 网格:
微软当前建议测量值尽量使用 4 的倍数;窄窗口常用 12 epx gutter,宽窗口常用 24 epx gutter。
可以把常用 Thickness 放入资源:
<Thickness x:Key="Space8">8</Thickness>
<Thickness x:Key="Space12">12</Thickness>
<Thickness x:Key="Space16">16</Thickness>
<Thickness x:Key="PagePadding">24</Thickness>
3. 字体层级¶
Windows 当前推荐 Segoe UI Variable。中文会根据系统字体回退,不建议为整个应用强制使用不完整的西文字体。
推荐控制在四到六级:
| 用途 | 建议字号/行高 | 字重 |
|---|---|---|
| Caption | 12 / 16 | Regular |
| Body | 14 / 20 | Regular |
| Body Strong | 14 / 20 | Semibold |
| Subtitle | 20 / 28 | Semibold |
| Title | 28 / 36 | Semibold |
| Large Title | 40 / 52 | Semibold |
实际企业桌面应用通常只需 Caption、Body、Subtitle、Title 四级。默认左对齐,正文避免大面积居中;强调优先用 Semibold,少用全粗体和斜体。
<Style x:Key="BodyTextStyle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI Variable" />
<Setter Property="FontSize" Value="14" />
<Setter Property="LineHeight" Value="20" />
<Setter Property="Foreground" Value="{DynamicResource AppTextPrimaryBrush}" />
</Style>
<Style x:Key="PageTitleStyle"
TargetType="TextBlock"
BasedOn="{StaticResource BodyTextStyle}">
<Setter Property="FontSize" Value="28" />
<Setter Property="LineHeight" Value="36" />
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
4. 圆角与层级¶
建议只保留两三个圆角级别:
使用留白、背景表面和细边框表达层级,阴影只用于真正浮在上方的内容。不要给每个面板都加厚重阴影。
Card 示例:
<Style x:Key="CardStyle" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource AppSurfaceBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AppBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Padding" Value="16" />
</Style>
5. 图标¶
- Fluent 应用优先采用 Segoe Fluent Icons 或一套统一的矢量图标库。
- 不要混合描边、填充、彩色 Emoji 和不同粗细的图标。
- 常用工具栏图标保持一致尺寸,例如 16 或 20 epx。
- 仅图标按钮必须提供 Tooltip 和可访问名称。
- 不要依靠图标本身表达难以猜测的业务动作。
三、资源字典的正确组织方式¶
App.xaml 只负责组合,不放几千行控件模板:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Tokens.Colors.xaml" />
<ResourceDictionary Source="Themes/Tokens.Spacing.xaml" />
<ResourceDictionary Source="Themes/Tokens.Typography.xaml" />
<ResourceDictionary Source="Themes/Controls.Button.xaml" />
<ResourceDictionary Source="Themes/Controls.Input.xaml" />
<ResourceDictionary Source="Themes/Controls.DataGrid.xaml" />
<ResourceDictionary Source="Themes/Components.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
后合并的字典可以覆盖前面的同名资源,因此顺序应保持:
建议:
- 全局隐式 Style 只修改真正需要全局统一的属性。
- 主要、次要、危险按钮使用显式 Style Key。
- 页面专用样式放在页面作用域,避免污染全局。
- 业务组件重复出现三次以上,再提取为 UserControl 或 CustomControl。
- 只改 Padding、字体、颜色时用 Style;改变视觉树时才写 ControlTemplate。
四、基于默认控件扩展,而不是全部推倒重写¶
主要按钮示例:
<Style x:Key="PrimaryButtonStyle"
TargetType="Button"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="MinHeight" Value="32" />
<Setter Property="Padding" Value="16,6" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
这样保留 Fluent 默认模板中的 Hover、Pressed、Disabled、Focus 和高对比度处理。
只有产品确实需要不同的结构时,才复制并修改完整 ControlTemplate。一旦重写模板,就必须自行保证:
- Normal、MouseOver、Pressed、Disabled。
- Keyboard Focus 和 FocusVisual。
- Selected、Checked、Indeterminate。
- Validation Error。
- 高对比度和系统颜色。
- 动画关闭或系统减少动画时的行为。
- AutomationProperties 和键盘可访问性。
很多“好看但不好用”的 WPF 主题,问题就在于只画了 Normal 和 MouseOver 两个状态。
五、页面布局方法¶
推荐页面骨架¶
Window
├─ Navigation / Command area
└─ Page
├─ Header:标题、说明、主要操作
├─ Toolbar / Filters
├─ Main content:列表、表单或详情
└─ Status / Secondary actions
布局规则:
- 优先
Grid的Auto、*、Min/MaxWidth,不要大量固定 Width/Height。 - 用留白分组,少用 GroupBox 和粗边框把所有内容框起来。
- 页面左右边距统一为 24;窄窗口可降到 12。
- 表单标签宽度一致,输入控件左边缘对齐。
- 一个页面只保留一个最突出主要操作。
- 危险操作与普通操作在空间或视觉上分离。
- 内容区可拉伸,工具栏和状态栏保持自然高度。
- 窗口缩小时采用 Reflow、折叠次要内容或切换布局,不要只出现水平滚动条。
数据密集型桌面应用¶
好看不等于把 DataGrid 做成大卡片。桌面业务软件更重要的是信息密度和可扫描性:
- 行高保持紧凑但可点击。
- 数字右对齐,文本左对齐,状态列居中。
- 表头与数据使用清晰但克制的对比。
- 斑马纹只在确实提升阅读时使用,透明度要低。
- 选中、悬停、编辑、校验错误必须容易区分。
- 空状态、加载状态、失败状态要有正式设计。
- 常用操作直接显示,低频操作放菜单。
六、主题切换¶
推荐至少支持:
System:跟随系统。Light。Dark。
主题切换时,只替换颜色和必要的表面资源,不要重新创建整个窗口。Light/Dark 字典应提供相同资源 Key。
切换后的验证项目:
- 所有文本都有足够对比度。
- Disabled 状态仍然可辨认。
- Popup、ContextMenu、ToolTip、Dialog 与主窗口主题一致。
- 图标没有写死黑色或白色。
- DataGrid 表头、滚动条、选中行和错误提示正确。
- 第三方控件同步切换。
七、性能和维护¶
- 不要在大量列表项上使用
DropShadowEffect、模糊和复杂渐变。 - DataGrid、ListBox 和 ItemsControl 保持 UI 虚拟化。
- 避免过深的 ControlTemplate 和多层无意义 Border/Grid。
- 静态、不切换的资源优先
StaticResource。 - 大图片按显示尺寸解码,避免直接载入超大原图。
- 动画只用于状态反馈和空间关系,持续时间短,不阻塞交互。
- 为控件样式建立一个独立的 Gallery/Showcase 页面,集中展示所有状态。
- 主题库升级前做视觉回归,避免 NuGet 更新导致全局样式改变。
八、必须测试的视觉矩阵¶
主题:Light / Dark / High Contrast
DPI:100% / 125% / 150% / 200%
窗口:最小宽度 / 常用宽度 / 最大化 / 多显示器
输入:鼠标 / 键盘 / 触摸(如果支持)
语言:中文 / 英文 / 长文本 / 缺失字体
状态:Normal / Hover / Pressed / Focus / Disabled
业务:Loading / Empty / Error / Validation / Offline
同时检查 Tab 顺序、焦点框、屏幕阅读器名称、文本缩放和仅使用键盘能否完成核心操作。
九、常见失败方式¶
- 每个页面自己写颜色、Margin 和 FontSize。
- 一个应用里同时使用 Fluent、Material、Metro 和自定义图标。
- 为了圆角而重写所有 ControlTemplate。
- 用大面积品牌色、渐变、发光和阴影制造“科技感”。
- 删除 FocusVisualStyle,使键盘用户看不到焦点。
- 所有窗口和控件使用固定像素尺寸。
- 只设计正常状态,忽略禁用、错误、空数据和加载状态。
- 使用 Emoji 充当专业功能图标。
- 为追求留白而牺牲企业软件必要的信息密度。
- 直接使用网上复制的主题,不检查许可证、维护状态和可访问性。
十、推荐实施顺序¶
第一阶段:确定设计方向¶
- 以 WPF Gallery 和 Windows Fluent 指南为参考。
- 选择 2~3 个最具代表性的页面:主列表、编辑表单、详情/仪表盘。
- 先做低保真布局,再确定颜色和控件外观。
第二阶段:建立设计系统¶
- 定义 Light/Dark 语义颜色。
- 定义四级字体和 4 倍数间距。
- 定义 Button、Input、Card、DataGrid、Dialog 的外观和状态。
- 建立 UI Gallery 页面展示全部控件状态。
第三阶段:落地页面¶
- 页面只组合设计系统中的资源和组件。
- 发现重复模式后再提取业务组件。
- 每完成一个页面就验证 DPI、缩放、主题和键盘。
第四阶段:质量与迭代¶
- 让真实用户完成任务,而不是只让团队评价截图。
- 记录任务耗时、误操作和找不到入口的位置。
- 优先改善信息层级、文案和操作路径,再调整装饰细节。
- 为主题和关键页面增加截图或人工视觉回归检查。
对当前技术栈的具体建议¶
当前计划是 .NET 10 LTS + WPF + CommunityToolkit.Mvvm,建议采用:
官方 WPF Fluent Theme
↓
自定义 Colors / Typography / Spacing Tokens
↓
少量基于默认样式的 Button / Input / Card / DataGrid 样式
↓
MVVM 驱动 Loading / Empty / Error / Validation 等页面状态
↓
只有缺少关键组件时,才评估 WPF UI 或商业控件库
这条路线既能保持现代 Windows 观感,也不会过度依赖第三方主题。对于企业桌面软件,比从零制作整套 ControlTemplate 更稳定、更容易维护。