23 KiB
AtomUI 主题定制 — 开发者指南
本文档面向 AtomUI 应用开发者,提供主题定制的完整实践指南:从全局主题配置到局部覆盖,从 Shared Token 修改到组件级 Token 定制。配合 Gallery 示例代码进行说明。如需了解底层原理,请参阅 ThemeCustomizationPrinciples.md。
1. 快速开始:主题定制能力概览
AtomUI 提供以下四种主题定制方式,按作用范围从大到小排列:
| 定制方式 | 作用范围 | 配置方式 | 典型场景 |
|---|---|---|---|
| 全局主题 XML | 整个应用 | XML 定义文件 | 品牌主色、全局圆角、全局字号 |
| 全局 Shared Token 覆盖 | ThemeConfigProvider 子树 |
AXAML SharedTokenSetters |
页面区域使用暗色主题 |
| 组件级 Token 覆盖 | 特定组件类型 | AXAML ControlTokenInfoSetters |
让所有 Button 使用绿色主色 |
| 算法切换 | ThemeConfigProvider 子树 |
AXAML Algorithms |
局部暗色/紧凑模式 |
2. 修改全局 Shared Token
2.1 在 AXAML 中使用 ThemeConfigProvider
最常见的定制方式是通过 ThemeConfigProvider 修改 Seed Token。修改一个 Seed Token(如 ColorPrimary),所有由它派生的梯度 Token 会自动重新计算。
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<!-- Seed Token,影响范围大 -->
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
<atom:TokenSetter Key="BorderRadius" Value="2" />
<!-- Map/Alias Token,影响范围小 -->
<atom:TokenSetter Key="ColorBgContainer" Value="#f6ffed" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<!-- 此范围内的所有控件都使用修改后的 Token -->
<WrapPanel>
<atom:Button ButtonType="Primary">Primary Button</atom:Button>
<atom:Button>Default Button</atom:Button>
</WrapPanel>
</atom:ThemeConfigProvider>
效果:ThemeConfigProvider 内的按钮会使用绿色(#00b96b)作为主色,圆角为 2px,背景色为浅绿色。ThemeConfigProvider 外的控件不受影响。
2.2 常用 Seed Token
| Token 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ColorPrimary |
Color |
#1677ff |
品牌主色,影响所有主色系梯度 |
ColorSuccess |
Color |
#52c41a |
成功色 |
ColorWarning |
Color |
#faad14 |
警戒色 |
ColorError |
Color |
#ff4d4f |
错误色 |
ColorInfo |
Color |
#1677ff |
信息色 |
FontSize |
double |
14 |
基准字号,影响所有字号梯度 |
BorderRadius |
CornerRadius |
6 |
基础圆角 |
SizeUnit |
double |
4 |
尺寸变化单位 |
SizeStep |
double |
4 |
尺寸步长 |
ControlHeight |
double |
32 |
基础控件高度 |
LineWidth |
double |
1 |
基础线宽 |
EnableMotion |
bool |
true |
是否启用动画 |
提示:完整的 Token 列表请参考 TokenArchitecture.md。修改 Seed Token 是最高效的定制方式 — 修改一个值即可级联影响数十个派生值。
3. 使用预设算法
3.1 算法切换
通过 Algorithms 属性可以切换主题算法,实现暗色或紧凑布局:
<!-- 暗色主题 -->
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.Algorithms>
<x:String>Dark</x:String>
</atom:ThemeConfigProvider.Algorithms>
<Border Background="#2b2d30" Padding="20">
<StackPanel Orientation="Horizontal" Spacing="5">
<atom:LineEdit PlaceholderText="Please input" Width="200" />
<atom:Button ButtonType="Primary">Submit</atom:Button>
</StackPanel>
</Border>
</atom:ThemeConfigProvider>
3.2 可用算法
| 算法名称 | 说明 |
|---|---|
Default |
默认亮色算法(始终包含,无需显式声明) |
Dark |
暗色算法 |
Compact |
紧凑算法(更小的尺寸和间距) |
算法可以组合使用。例如同时启用暗色和紧凑:
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.Algorithms>
<x:String>Dark</x:String>
<x:String>Compact</x:String>
</atom:ThemeConfigProvider.Algorithms>
<!-- 暗色 + 紧凑布局 -->
</atom:ThemeConfigProvider>
3.3 算法与 Token 覆盖组合
算法和 Token 覆盖可以同时使用:
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.Algorithms>
<x:String>Dark</x:String>
</atom:ThemeConfigProvider.Algorithms>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<!-- 暗色主题 + 绿色主色 -->
</atom:ThemeConfigProvider>
4. 修改组件 Token
4.1 组件级 Shared Token 覆盖
通过 ControlTokenInfoSetters 可以修改特定组件消费的 Shared Token,不影响其他组件:
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.ControlTokenInfoSetters>
<!-- 只修改 Button 的主色 -->
<atom:ControlTokenInfoSetter TokenId="Button" EnableAlgorithm="True">
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ControlTokenInfoSetter>
<!-- 只修改 AddOnDecoratedBox(输入框装饰器)的主色 -->
<atom:ControlTokenInfoSetter TokenId="AddOnDecoratedBox" EnableAlgorithm="True">
<atom:TokenSetter Key="ColorPrimary" Value="#eb2f96" />
</atom:ControlTokenInfoSetter>
</atom:ThemeConfigProvider.ControlTokenInfoSetters>
<StackPanel Orientation="Horizontal" Spacing="5">
<atom:LineEdit PlaceholderText="Please input" Width="200" />
<atom:Button ButtonType="Primary">Submit</atom:Button>
</StackPanel>
</atom:ThemeConfigProvider>
效果:Button 使用绿色主色,输入框装饰使用粉色主色,其他控件保持全局默认。
4.2 EnableAlgorithm 的作用
EnableAlgorithm 控制覆盖的 Shared Token 是否经过算法重新派生:
EnableAlgorithm |
行为 | 适用场景 |
|---|---|---|
True |
修改 ColorPrimary 后,ColorPrimaryHover、ColorPrimaryBg 等梯度色自动重算 |
需要完整色系联动时 |
False(默认) |
只有被显式修改的 Token 值变化,其他保持全局值 | 精确控制单个值时 |
对比示例(来自 Gallery):
<!-- EnableAlgorithm=True:修改 ColorPrimary 后 Hover/Active 等梯度色自动联动 -->
<atom:ControlTokenInfoSetter TokenId="Button" EnableAlgorithm="True">
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ControlTokenInfoSetter>
<!-- EnableAlgorithm=False:只有 ColorPrimary 本身变化,Hover 等保持默认 -->
<atom:ControlTokenInfoSetter TokenId="Button">
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ControlTokenInfoSetter>
4.3 组件自身 Token(ControlTokenSetter)
要修改组件自身的专属 Token(非 Shared Token),使用 ControlTokenSetter:
<atom:ControlTokenInfoSetter TokenId="Button">
<!-- 修改组件消费的 Shared Token -->
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
<!-- 修改组件自身的专属 Token -->
<atom:ControlTokenSetter Key="PaddingInline" Value="20" />
</atom:ControlTokenInfoSetter>
注意:
TokenSetter和ControlTokenSetter是不同的类型。TokenSetter用于 Shared Token(Seed/Map/Alias),ControlTokenSetter用于组件自身的 Component Token。
4.4 常见的 TokenId
TokenId 对应组件 Token 类中的 const string ID。常见值:
| TokenId | 对应组件 |
|---|---|
Button |
按钮 |
LineEdit |
单行输入框 |
AddOnDecoratedBox |
输入框装饰器 |
CheckBox |
复选框 |
Radio |
单选按钮 |
Select |
选择器 |
Tag |
标签 |
Alert |
警告提示 |
Badge |
徽标 |
Tabs |
标签页 |
要查看某个组件的 TokenId,请查看对应的
*Token.cs文件中的public const string ID常量。
5. 嵌套主题
5.1 基本嵌套
ThemeConfigProvider 可以任意层级嵌套,每层定义自己的 Token 覆盖:
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="ColorPrimary" Value="#1677ff" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<Border Padding="20">
<StackPanel Orientation="Horizontal" Spacing="5">
<!-- 使用 #1677ff 蓝色 -->
<atom:Button ButtonType="Primary">Theme 1</atom:Button>
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<!-- 使用 #00b96b 绿色 -->
<atom:Button ButtonType="Primary">Theme 2</atom:Button>
</atom:ThemeConfigProvider>
</StackPanel>
</Border>
</atom:ThemeConfigProvider>
5.2 典型场景
- 侧边栏暗色:主内容区使用亮色,侧边栏嵌套暗色
ThemeConfigProvider。 - 卡片高亮:某张卡片使用不同的品牌色突出显示。
- 表单区域:表单区域使用紧凑布局,其他区域保持默认。
6. 全局主题 XML 定义文件
6.1 创建自定义主题文件
创建 XML 文件(如 MyBrand.xml):
<?xml version="1.0" encoding="UTF-8"?>
<Theme Name="My Brand Theme" IsDefault="false">
<Algorithms>Default</Algorithms>
<SharedTokens>
<Token Name="ColorPrimary" Value="#722ed1" />
<Token Name="ColorSuccess" Value="#13c2c2" />
<Token Name="BorderRadius" Value="8" />
<Token Name="FontSize" Value="16" />
</SharedTokens>
<ControlTokens>
<ControlToken Id="Button" EnableAlgorithm="true">
<Token Name="ColorPrimary" Value="#722ed1" IsShared="true" />
</ControlToken>
</ControlTokens>
</Theme>
6.2 XML Schema 参考
<Theme> 根元素:
| 属性 | 必填 | 说明 |
|---|---|---|
Name |
是 | 主题显示名称 |
IsDefault |
是 | 是否为默认主题(true/false) |
<Algorithms> 元素:
- 内容为逗号分隔的算法名:
Default、Dark、Compact。 - 示例:
<Algorithms>Default,Dark</Algorithms>
<SharedTokens> 子元素 <Token>:
| 属性 | 必填 | 说明 |
|---|---|---|
Name |
是 | Token 属性名(如 ColorPrimary) |
Value |
是 | Token 值(字符串格式,自动转换) |
<ControlTokens> → <ControlToken> 子元素:
| 属性 | 必填 | 说明 |
|---|---|---|
Id |
是 | 组件 Token ID(如 Button) |
EnableAlgorithm |
否 | 是否启用算法派生(默认 false) |
<ControlToken> 内的 <Token>:
| 属性 | 必填 | 说明 |
|---|---|---|
Name |
是 | Token 名称 |
Value |
是 | Token 值 |
IsShared |
否 | true 表示覆盖 Shared Token,false/省略 表示覆盖组件自身 Token |
6.3 注册自定义主题
将主题文件放置在以下位置之一:
- 应用数据目录:
{AppData}/{AppName}/Themes/MyBrand.xml— 自动扫描。 - 自定义目录:通过
ThemeManager.AddCustomThemePaths()注册。 - 嵌入资源:通过
IThemeAssetPathProvider注册(库开发者模式)。
6.4 激活自定义主题
this.UseAtomUI(builder =>
{
builder.UseDesktopControls();
builder.WithDefaultTheme("MyBrand"); // 使用主题文件名(不含扩展名)
});
7. 关键 API 参考
7.1 AXAML 类型与标记扩展
| 类型 | AXAML 标签 | 说明 |
|---|---|---|
ThemeConfigProvider |
<atom:ThemeConfigProvider> |
局部主题配置容器 |
TokenSetter |
<atom:TokenSetter> |
Shared Token 设置器 |
ControlTokenSetter |
<atom:ControlTokenSetter> |
组件自身 Token 设置器 |
ControlTokenInfoSetter |
<atom:ControlTokenInfoSetter> |
组件级 Token 配置块 |
SharedTokenResourceExtension |
{atom:SharedTokenResource ...} |
全局 Token 资源引用 |
TokenResourceExtension<T> |
{atom:ButtonTokenResource ...} 等 |
组件 Token 资源引用 |
7.2 ThemeConfigProvider 属性
| 属性 | 类型 | 说明 |
|---|---|---|
Content |
Control? |
子内容([Content] 标记,可直接写子控件) |
Algorithms |
List<string> |
算法列表("Default", "Dark", "Compact") |
SharedTokenSetters |
List<TokenSetter> |
全局 Token 覆盖列表 |
ControlTokenInfoSetters |
List<ControlTokenInfoSetter> |
组件级 Token 覆盖列表 |
7.3 TokenSetter 属性
| 属性 | 类型 | 说明 |
|---|---|---|
Key |
string |
Token 属性名(如 "ColorPrimary") |
Value |
string |
Token 值(字符串格式) |
7.4 ControlTokenInfoSetter 属性
| 属性 | 类型 | 说明 |
|---|---|---|
TokenId |
string |
组件 Token ID |
EnableAlgorithm |
bool |
是否对覆盖的 Shared Token 启用算法派生(默认 false) |
Setters |
List<TokenSetter> |
Token 设置器列表(支持 TokenSetter 和 ControlTokenSetter) |
7.5 C# API
// IThemeManagerBuilder — 应用启动时配置
public interface IThemeManagerBuilder
{
void WithDefaultTheme(string themeId); // 设置默认主题
void WithDefaultFontFamily(FontFamily fontFamily); // 覆盖默认字体
void WithDefaultFontFamily(string fontFamily);
void WithThemeVariantCalculatorFactory(IThemeVariantCalculatorFactory factory); // 自定义算法工厂
}
// IThemeManager — 运行时访问
public interface IThemeManager
{
IReadOnlyCollection<ITheme> AvailableThemes { get; } // 所有可用主题
ITheme? ActivatedTheme { get; } // 当前激活的主题
bool IsDarkThemeMode { get; set; } // 暗色模式开关
bool IsCompactThemeMode { get; set; } // 紧凑模式开关
bool IsMotionEnabled { get; set; } // 动效开关
}
// ITheme — 主题实例
public interface ITheme
{
DesignToken SharedToken { get; } // 全局 SharedToken
IControlDesignToken? GetControlToken(string tokenId); // 获取组件 Token
}
8. Gallery 示例解读
Gallery 应用中的 CustomizeThemeShowCase.axaml 展示了四个典型的主题定制场景。
8.1 示例 1:修改 Design Token
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
<atom:TokenSetter Key="BorderRadius" Value="2" />
<atom:TokenSetter Key="ColorBgContainer" Value="#f6ffed" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<WrapPanel>
<atom:Button ButtonType="Primary">Primary Button</atom:Button>
<atom:Button>Default Button</atom:Button>
</WrapPanel>
</atom:ThemeConfigProvider>
要点:
ColorPrimary是 Seed Token,修改后所有主色系梯度自动联动。BorderRadius是 Seed Token,修改后所有圆角梯度自动联动。ColorBgContainer是 Alias Token,只影响容器背景色这一个值。
8.2 示例 2:使用预设算法
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.Algorithms>
<x:String>Dark</x:String>
</atom:ThemeConfigProvider.Algorithms>
<Border Background="#2b2d30" Padding="20">
<StackPanel Orientation="Horizontal" Spacing="5">
<atom:LineEdit PlaceholderText="Please input" Width="200" />
<atom:Button ButtonType="Primary">Submit</atom:Button>
</StackPanel>
</Border>
</atom:ThemeConfigProvider>
要点:
Algorithms列表中添加"Dark"即可切换到暗色算法。- 暗色算法会自动反转色彩体系:浅色背景变深色,深色文字变浅色。
- 外层
Border设置深色背景以匹配暗色主题。
8.3 示例 3:定制组件 Token
<!-- EnableAlgorithm=True:自动派生梯度色 -->
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.ControlTokenInfoSetters>
<atom:ControlTokenInfoSetter TokenId="Button" EnableAlgorithm="True">
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ControlTokenInfoSetter>
<atom:ControlTokenInfoSetter TokenId="AddOnDecoratedBox" EnableAlgorithm="True">
<atom:TokenSetter Key="ColorPrimary" Value="#eb2f96" />
</atom:ControlTokenInfoSetter>
</atom:ThemeConfigProvider.ControlTokenInfoSetters>
<StackPanel Orientation="Horizontal" Spacing="5">
<atom:LineEdit PlaceholderText="Please input" Width="200" />
<atom:Button ButtonType="Primary">Submit</atom:Button>
</StackPanel>
</atom:ThemeConfigProvider>
<!-- EnableAlgorithm=False:直接覆盖,不派生 -->
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.ControlTokenInfoSetters>
<atom:ControlTokenInfoSetter TokenId="Button">
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ControlTokenInfoSetter>
</atom:ThemeConfigProvider.ControlTokenInfoSetters>
...
</atom:ThemeConfigProvider>
要点:
- 不同组件可以使用不同的主色,互不影响。
EnableAlgorithm="True"时 Hover/Active 等交互色会自动联动。
8.4 示例 4:嵌套主题
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="ColorPrimary" Value="#1677ff" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<StackPanel Orientation="Horizontal" Spacing="5">
<atom:Button ButtonType="Primary">Theme 1</atom:Button>
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<atom:Button ButtonType="Primary">Theme 2</atom:Button>
</atom:ThemeConfigProvider>
</StackPanel>
</atom:ThemeConfigProvider>
要点:
- "Theme 1" 按钮使用外层蓝色(
#1677ff)。 - "Theme 2" 按钮使用内层绿色(
#00b96b)。 - 嵌套层的 Token 不继承父层 — 未设置的 Token 使用框架默认值。
9. 运行时主题切换
9.1 全局暗色/紧凑模式切换
通过 IThemeManager 可以在运行时切换暗色模式和紧凑模式:
var themeManager = AvaloniaLocator.Current.GetRequiredService<IThemeManager>();
// 切换暗色模式
themeManager.IsDarkThemeMode = true;
// 切换紧凑模式
themeManager.IsCompactThemeMode = true;
// 组合:暗色 + 紧凑
themeManager.IsDarkThemeMode = true;
themeManager.IsCompactThemeMode = true;
9.2 切换主题变体
// 切换到特定主题变体
Application.Current.RequestedThemeVariant =
Theme.BuildThemeVariant("DaybreakBlue", new List<ThemeAlgorithm>
{
ThemeAlgorithm.Default,
ThemeAlgorithm.Dark
});
9.3 禁用动画
var themeManager = AvaloniaLocator.Current.GetRequiredService<IThemeManager>();
themeManager.IsMotionEnabled = false;
或在 AXAML 中通过 Token 覆盖:
<atom:ThemeConfigProvider>
<atom:ThemeConfigProvider.SharedTokenSetters>
<atom:TokenSetter Key="EnableMotion" Value="false" />
</atom:ThemeConfigProvider.SharedTokenSetters>
<!-- 此范围内的控件动画被禁用 -->
</atom:ThemeConfigProvider>
10. 常见问题与注意事项
10.1 Token 名称必须精确匹配
TokenSetter 的 Key 必须与 DesignToken 类中的属性名完全一致(PascalCase)。常见错误:
<!-- ❌ 错误:使用了 camelCase -->
<atom:TokenSetter Key="colorPrimary" Value="#00b96b" />
<!-- ✅ 正确:使用 PascalCase -->
<atom:TokenSetter Key="ColorPrimary" Value="#00b96b" />
10.2 值类型自动转换
Token 值在 XML/AXAML 中以字符串形式提供,AtomUI 内置的 ITokenValueConverter 会自动转换为目标类型:
| 字符串格式 | 转换为 | 示例 |
|---|---|---|
#1677ff |
Color |
十六进制颜色 |
red, blue |
Color |
颜色名称 |
14 |
double |
数值 |
6 |
CornerRadius |
圆角(统一四角) |
true / false |
bool |
布尔值 |
1 |
double (LineWidth) |
线宽 |
10.3 ThemeConfigProvider 的独立性
每个 ThemeConfigProvider 都是独立计算的 — 它不继承父级 ThemeConfigProvider 的 Token 值。未显式设置的 Token 使用 DesignToken 类中的代码默认值(即 Ant Design 5.0 标准值)。
如果你希望只修改某几个 Token 而保持其他不变,直接设置那几个即可 — 其他 Token 会使用标准默认值。
10.4 性能考虑
ThemeConfigProvider的计算开销:每个ThemeConfigProvider实例会创建独立的DesignToken和所有注册的ControlDesignToken实例,并走完整的算法推导流程。在页面上大量使用ThemeConfigProvider可能有性能影响。- 建议:尽量在较高层级使用
ThemeConfigProvider,避免在列表项等重复元素中使用。 - 组件级覆盖 相对轻量,因为只 Clone 和重算目标组件的 SharedToken。
10.5 TokenSetter vs ControlTokenSetter
在 ControlTokenInfoSetter 内部,正确区分两种 Setter:
| Setter 类型 | 用于 | 示例 |
|---|---|---|
<atom:TokenSetter> |
覆盖组件消费的 Shared Token | ColorPrimary, FontSize, BorderRadius |
<atom:ControlTokenSetter> |
覆盖组件自身的 Component Token | PaddingInline, PrimaryColor, DangerColor |
10.6 多主题切换与持久化
AtomUI 的 ThemePool 包含所有已发现主题的算法组合变体。切换主题时:
- 目标主题会按需加载(首次激活时加载)。
- 已加载的主题会缓存在
ThemePool中。 - 主题切换通过 Avalonia 的
ThemeDictionaries机制实现,无需重启。
如需持久化用户的主题选择,应用层需要自行保存主题 ID 和算法组合,并在下次启动时通过 WithDefaultTheme() 恢复。
11. 文档索引
| 文档 | 描述 |
|---|---|
| Overview.md | 主题系统总览 |
| TokenArchitecture.md | Design Token 四层架构详解 |
| ThemeAlgorithm.md | 主题算法与调色板生成 |
| ThemeManagement.md | 主题管理器与主题生命周期 |
| TokenResourceBinding.md | Token 资源绑定与 Source Generator |
| ThemeCustomizationPrinciples.md | 主题自定义原理说明(本指南的姐妹篇) |
| ThemeCustomizationGuide.md | 本文件 — 主题定制开发者指南 |