跳转至

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. 语义颜色

不要命名为 Blue500BrushGray100Brush,优先根据用途命名:

<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 引用:

<Border Background="{DynamicResource AppSurfaceBrush}" />

规则:

  • 需要运行时切换主题的颜色使用 DynamicResource
  • 不会变化的尺寸、模板和结构优先使用 StaticResource
  • 强调色只用于主要操作、选中和焦点,不要让整页到处都是品牌色。
  • 错误不能只用红色表达,还应配合图标和文字。
  • 亮色和暗色不是简单把 RGB 取反,应分别调节表面层级、边框和文本透明度。

2. 间距令牌

采用 4 epx 网格:

4   极小间距
8   紧密控件间距
12  小型分组
16  常规容器内边距
24  页面边距和大分组
32  大区块间距
48  页面级留白

微软当前建议测量值尽量使用 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. 圆角与层级

建议只保留两三个圆角级别:

4  小型控件
8  Card、菜单、浮层
12 对话框或大容器(少量使用)

使用留白、背景表面和细边框表达层级,阴影只用于真正浮在上方的内容。不要给每个面板都加厚重阴影。

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

布局规则:

  • 优先 GridAuto*、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 充当专业功能图标。
  • 为追求留白而牺牲企业软件必要的信息密度。
  • 直接使用网上复制的主题,不检查许可证、维护状态和可访问性。

十、推荐实施顺序

第一阶段:确定设计方向

  1. 以 WPF Gallery 和 Windows Fluent 指南为参考。
  2. 选择 2~3 个最具代表性的页面:主列表、编辑表单、详情/仪表盘。
  3. 先做低保真布局,再确定颜色和控件外观。

第二阶段:建立设计系统

  1. 定义 Light/Dark 语义颜色。
  2. 定义四级字体和 4 倍数间距。
  3. 定义 Button、Input、Card、DataGrid、Dialog 的外观和状态。
  4. 建立 UI Gallery 页面展示全部控件状态。

第三阶段:落地页面

  1. 页面只组合设计系统中的资源和组件。
  2. 发现重复模式后再提取业务组件。
  3. 每完成一个页面就验证 DPI、缩放、主题和键盘。

第四阶段:质量与迭代

  1. 让真实用户完成任务,而不是只让团队评价截图。
  2. 记录任务耗时、误操作和找不到入口的位置。
  3. 优先改善信息层级、文案和操作路径,再调整装饰细节。
  4. 为主题和关键页面增加截图或人工视觉回归检查。

对当前技术栈的具体建议

当前计划是 .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 更稳定、更容易维护。

官方和项目资料