8.5 KiB
Empty 空状态
概述
空状态(Empty)是一个用于展示「当前区域暂无数据或内容」的占位控件。当列表、表格、卡片等数据容器中没有可显示的内容时,Empty 提供一个友好的视觉提示,帮助用户理解当前状态并减少空白带来的迷惑感。它是 Ant Design Empty 组件的 AtomUI 实现。
AtomUI 的 Empty 控件完整复刻了 Ant Design 5.0 Empty 的设计规范,支持两种预设图片(Default / Simple)、自定义图片源、描述文字、三种尺寸等特性。
设计原理
Ant Design 的空状态设计哲学
Ant Design 认为「空状态」是界面中不可忽视的重要组成部分。空数据不应该以完全空白的方式呈现——这会让用户困惑「是数据还没加载?是出错了?还是确实没有数据?」。一个好的空状态展示应该:
| 设计维度 | 设计意图 |
|---|---|
| 明确传达 | 通过图片 + 文字清楚告诉用户「这里暂无数据」 |
| 视觉友好 | 使用柔和的插图和灰色调,不抢占视觉焦点,避免给用户造成焦虑 |
| 尺寸灵活 | 支持不同场景的尺寸需求——页面级空状态用大图,表格/列表内嵌用小图 |
| 可定制 | 支持自定义图片和描述文字,适应不同业务语境 |
Ant Design 提供了两种预设图片风格:
| 预设类型 | 设计特征 | 典型用途 |
|---|---|---|
| Default(默认) | 彩色插图,尺寸较大,细节丰富 | 页面级空状态、独立空数据提示 |
| Simple(简洁) | 灰色线条图,尺寸较小,简约 | 列表/表格内嵌空状态、紧凑场景 |
Avalonia 对标
Avalonia 框架本身没有内置的 Empty 空状态控件。AtomUI 的 Empty 基于 TemplatedControl 从零构建,使用 SVG 渲染预设图片,支持主题色动态适配。
AtomUI 的实现设计
AtomUI 的 Empty 控件采用 二层继承模型:
| 层级 | 类 | 职责 |
|---|---|---|
| 基类层 | AbstractEmpty(AtomUI.Controls) |
设备无关的行为、属性、图片渲染逻辑 |
| 平台层 | Empty(AtomUI.Desktop.Controls) |
桌面端 Token 注册 |
核心实现特点:
- SVG 动态生成:预设图片不是静态文件,而是通过
BuiltInImageBuilder在运行时根据当前主题色动态生成 SVG 源码。这使得预设图片能自动适配亮色/暗色主题 - 颜色适配:预设图片的各个颜色通道(边框色、阴影色、内容色、背景色)均从
SharedToken派生,确保在主题切换时自然过渡 - 三种图片源:支持预设图片(
PresetImage)、自定义 SVG 文件路径(ImagePath)、自定义 SVG 内容字符串(ImageSource)三种互斥方式 - 尺寸系统:通过
ISizeTypeAware接口支持 Large / Middle / Small 三种尺寸,自动调整图片高度和描述文字间距
功能详解
预设图片
Empty 提供两种内置的预设图片,通过 PresetImage 枚举属性选择:
| 枚举值 | 说明 | 视觉特征 |
|---|---|---|
PresetImage.Default |
默认空状态图 | 彩色,较大,包含多色渐变和阴影细节 |
PresetImage.Simple |
简洁空状态图 | 灰色线条,较小,简约无填充 |
预设图片通过 BuiltInImageBuilder 动态生成 SVG:
Default图片调用BuildDefaultImage(shadowColor, borderColor, borderColorSecondary)Simple图片调用BuildSimpleImage(contentColor, borderColor, shadowColor)
这些颜色参数均从当前主题的 SharedToken 派生,确保图片与主题一致。
自定义图片
当预设图片不满足需求时,可以使用自定义图片:
ImagePath:SVG 文件路径(支持avares://协议),适合打包在应用资源中的图片ImageSource:SVG 内容字符串,适合动态生成或从网络获取的图片
⚠️
PresetImage、ImagePath、ImageSource三者互斥,同时设置多个会抛出ApplicationException。
描述文字
通过 Description 属性设置描述文字,默认值通过本地化系统自动设置为 "No Data"(中文环境为 "暂无数据")。可通过 IsShowDescription=False 隐藏描述文字。
尺寸系统
Empty 实现了 ISizeTypeAware 接口,支持三种尺寸:
| 尺寸 | 图片高度 Token | 描述文字间距 Token |
|---|---|---|
Large |
EmptyImgHeight(ControlHeightLG × 2.5) |
DescriptionMargin |
Middle |
EmptyImgHeightMD(ControlHeightLG × 1.85) |
DescriptionMarginSM |
Small |
EmptyImgHeightSM(ControlHeightLG × 0.875) |
DescriptionMarginSM |
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
默认预设图片 PRESENTED_IMAGE_DEFAULT |
✅ | ✅ PresetImage="Default" |
✅ 完全对齐 |
简洁预设图片 PRESENTED_IMAGE_SIMPLE |
✅ | ✅ PresetImage="Simple" |
✅ 完全对齐 |
描述文字 description |
✅ ReactNode | ✅ Description(string) |
⚠️ 仅支持字符串,不支持任意 ReactNode |
自定义图片 image |
✅ ReactNode | ✅ ImagePath / ImageSource(SVG) |
⚠️ 仅支持 SVG 格式 |
图片尺寸 imageStyle |
✅ CSSProperties | ✅ SizeType(三种预设尺寸) |
⚠️ 不支持任意 CSS,改用枚举尺寸 |
| 主题色适配 | ✅ CSS 变量 | ✅ SVG 动态生成 + SharedToken | ✅ 完全对齐 |
| 暗色模式适配 | ✅ 自动 | ✅ CalculateTokenValues(isDarkMode) |
✅ 完全对齐 |
| 底部额外内容 | ✅ children slot |
❌ 暂未支持 | ⚠️ 可通过外部组合实现 |
继承关系
Avalonia.Controls.Primitives.TemplatedControl
└── AtomUI.Controls.AbstractEmpty
├── implements ISizeTypeAware
├── [TemplatePart("PART_SvgImage", typeof(Avalonia.Svg.Svg))]
└── AtomUI.Desktop.Controls.Empty
└── RegisterTokenResourceScope(EmptyToken.ScopeProvider)
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
TemplatedControl |
模板化控件基础设施、OnApplyTemplate |
AbstractEmpty(AtomUI.Controls) |
图片类型管理、SVG 动态生成、描述文字、尺寸接口、主题色绑定、图片源互斥验证 |
Empty(AtomUI.Desktop.Controls) |
桌面端 Token 作用域注册 |
实现的共享接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
ISizeTypeAware |
AtomUI.Controls.Shared |
支持 SizeType(Small / Middle / Large)三种尺寸切换 |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 基类 | src/AtomUI.Controls/Empty/AbstractEmpty.cs |
设备无关基类(行为、属性、SVG 渲染) |
| 预设图片 | src/AtomUI.Controls/Empty/BuiltInImageBuilder.cs |
内置 SVG 图片动态生成器 |
| 枚举 | src/AtomUI.Controls/Empty/PresetImage.cs |
PresetImage 枚举定义 |
| 控件类 | src/AtomUI.Desktop.Controls/Empty/Empty.cs |
桌面端 Empty 实现 |
| Token 定义 | src/AtomUI.Desktop.Controls/Empty/EmptyToken.cs |
组件级 Design Token |
| 主题模板 | src/AtomUI.Desktop.Controls/Empty/Themes/EmptyTheme.axaml |
ControlTheme + ResourceDictionary |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/DataDisplay/EmptyShowCase.axaml |
使用范例 |
模板结构
Empty 的 ControlTemplate 结构简洁,由一个垂直 StackPanel 包含图片和描述文字两部分:
StackPanel (Orientation=Vertical)
├── Svg (PART_SvgImage) ← SVG 图片渲染(预设图片或自定义 SVG)
└── TextBlock#Description ← 描述文字(支持换行)
关键绑定:
| 模板部件 | 绑定属性 | 说明 |
|---|---|---|
Svg#PART_SvgImage |
动态设置 Source / Path |
通过 SetupImage() 方法根据图片类型设置 |
TextBlock#Description |
{TemplateBinding Description} |
描述文字内容 |
Empty 自身 |
HorizontalAlignment="Center"、VerticalAlignment="Center" |
默认居中显示 |
主题色绑定(内部属性 → SharedToken):
| 内部属性 | SharedToken 资源键 | 用途 |
|---|---|---|
BorderColor |
ColorFill |
预设图片边框色 |
BorderColorSecondary |
ColorBorderSecondary |
预设图片次要边框色 |
ShadowColor |
ColorFillTertiary |
预设图片阴影色 |
ContentColor |
ColorFillQuaternary |
预设图片内容区域色 |
BgColor |
ColorBgElevated |
预设图片背景色(作为混合基色) |