12 KiB
RadioButton 单选框
概述
单选框(RadioButton)用于在一组互斥的选项中选择一个。与复选框不同,同一组内的单选框只能有一个被选中——选择新项时,之前的选中项自动取消。这种「互斥选择」的特性使其非常适合性别选择、支付方式、排序方式等场景。
AtomUI 的 RadioButton 控件复刻了 Ant Design 5.0 Radio 的设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。
设计原理
Ant Design 的单选框设计哲学
Ant Design 对单选框的定位是:「用于在多个备选项中选中单个状态」。其核心原则包括:
- 互斥选择 — 同组中只能有一个选中,选中新项时旧项自动取消
- 明确展示 — 所有选项同时可见,用户可以一次性比较所有选项
- 适中选项数 — 当选项数量较多(>5)时,建议使用 Select 下拉框代替
Radio 与 Radio.Group 的关系:
- 单独使用
Radio时,需手动分组管理 - 使用
Radio.Group时,自动管理组内的互斥逻辑
Radio 与 Radio.Button 的关系:
Radio呈现经典圆形指示器 + 文本的样式Radio.Button呈现按钮形态,常用于在几个选项间快速切换(如视图切换)
Avalonia RadioButton 基础能力
AtomUI 的 RadioButton 继承自 Avalonia 框架的 Avalonia.Controls.RadioButton(通过 AbstractRadioButton 中间层)。Avalonia RadioButton 继承自 ToggleButton,通过 GroupName 实现同组互斥。其继承链为:
Control → TemplatedControl → ContentControl → Button → ToggleButton → RadioButton
Avalonia RadioButton 提供的基础属性:
| 属性 | 说明 |
|---|---|
Content |
单选框旁的文本内容,可以是任意控件 |
IsChecked |
是否选中(bool? 类型,支持三态) |
GroupName |
分组名称,同名的 RadioButton 互斥 |
Command / CommandParameter |
MVVM 命令绑定 |
ClickMode |
控制 Click 事件触发时机 |
Avalonia RadioButton 提供的基础伪类:
| 伪类 | 说明 |
|---|---|
:checked |
选中状态 |
:unchecked |
未选中状态 |
:pointerover |
鼠标悬浮在控件上 |
:pressed |
控件被按下 |
:disabled |
IsEnabled == false |
:focus |
控件获得焦点 |
:focus-visible |
通过键盘(Tab)获得焦点 |
AtomUI 的扩展设计
AtomUI RadioButton 在 Avalonia RadioButton 的基础上,遵循 Ant Design 5.0 规范做了以下扩展:
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| 自定义指示器 | RadioIndicator 内部控件 |
精确复刻 Ant Design 的圆形指示器和选中动画效果 |
| 点击波纹 | IWaveSpiritAwareControl + WaveSpiritDecorator |
复刻 Ant Design 的 Wave 点击涟漪效果 |
| 过渡动画 | IsMotionEnabled + Transitions 动态配置 |
边框色、圆点大小、背景色平滑过渡 |
| 单选框组 | RadioButtonGroup 容器控件 |
对齐 Ant Design 的 Radio.Group,自动管理互斥 |
| 数据驱动分组 | RadioButtonGroup.ItemsSource + IRadioButtonOption |
支持绑定数据集合,动态生成选项 |
| 表单集成 | IFormItemAware 接口 |
可参与 FormItem 验证流程 |
| Design Token | RadioButtonToken + RegisterTokenResourceScope |
所有视觉值从 Token 派生,支持主题切换 |
功能详解
单选框组(RadioButtonGroup)
RadioButtonGroup 是管理一组单选框的容器控件,对应 Ant Design 的 Radio.Group:
- 自动管理组内互斥逻辑(选中新项时自动取消旧项)
- 通过
CheckedItem属性获取/设置当前选中项 - 支持
Orientation(水平/垂直排列)和ItemSpacing/LineSpacing控制间距 - 触发
CheckedChanged事件通知选中变化,提供OldCheckedItem和NewCheckedItem - 支持
ItemsSource+ItemTemplate数据驱动模式,通过IRadioButtonOption接口绑定数据 - 默认使用
WrapPanel作为布局面板,自动换行
声明式用法 vs 数据驱动用法:
- 声明式:直接在 AXAML 中声明
<atom:RadioButton>子元素 - 数据驱动:通过
ItemsSource绑定IRadioButtonOption集合,自动生成子项
指示器动画
RadioButton 使用自定义 RadioIndicator 控件渲染选中圆点:
- 未选中:空心圆环,背景为
ColorBgContainer,边框为ColorBorder - 选中:圆形指示器整体变为主色(
ColorPrimary),内部绘制白色实心圆点,带缩放过渡动画 - 悬浮:未选中状态下悬浮时,边框色变为
ColorPrimary - 禁用:背景灰色(
ColorBgContainerDisabled),选中圆点变为灰色(DotColorDisabled) - 波纹效果:选中时通过
WaveSpiritDecorator产生圆形波纹(CircleWave类型)
指示器的过渡动画包括:
RadioBorderBrush— 边框颜色平滑过渡RadioDotEffectSize— 圆点大小缩放过渡RadioBackground— 背景色快速过渡(使用MotionDurationFast)
按钮样式单选框(OptionButtonGroup)
Ant Design 的 Radio.Button 在 AtomUI 中通过独立的 OptionButtonGroup + OptionButton 控件实现,它们是独立控件,拥有独立的 Token 和主题,但在 Gallery 中与 RadioButton 在同一 ShowCase 中展示。OptionButton 的详细文档请参见 docs/Desktop.Controls/OptionButton/。
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 基本单选框 | ✅ <Radio> |
✅ <atom:RadioButton> |
✅ 完全对齐 |
禁用 disabled |
✅ 布尔属性 | ✅ IsEnabled |
✅ 完全对齐 |
单选框组 Radio.Group |
✅ 组容器 | ✅ RadioButtonGroup |
✅ 完全对齐 |
按钮样式 Radio.Button |
✅ 按钮形态 | ✅ OptionButtonGroup + OptionButton |
✅ 完全对齐 |
按钮填充样式 buttonStyle |
✅ outline/solid |
✅ ButtonStyle 属性(OptionButtonGroup) |
✅ 完全对齐 |
| 三种尺寸 | ✅ size 属性 |
✅ SizeType(仅 OptionButtonGroup) |
✅ 完全对齐 |
onChange |
✅ 回调 | ✅ CheckedChanged 事件 |
✅ 完全对齐 |
value / defaultValue |
✅ 受控/非受控 | ✅ CheckedItem 属性 |
✅ 完全对齐 |
| 自定义内容 | ✅ children |
✅ Content 属性 |
✅ 完全对齐 |
数据驱动 options |
✅ 数组 | ✅ ItemsSource + IRadioButtonOption |
✅ 完全对齐 |
继承关系
RadioButton
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ContentControl
└── Avalonia.Controls.Button
└── Avalonia.Controls.Primitives.ToggleButton
└── Avalonia.Controls.RadioButton
└── AtomUI.Controls.Commons.AbstractRadioButton
├── implements IWaveSpiritAwareControl
└── implements IFormItemAware
└── AtomUI.Desktop.Controls.RadioButton
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
ToggleButton / RadioButton |
选中/取消切换行为、IsChecked 状态、GroupName 同组互斥 |
AbstractRadioButton |
点击波纹接口 (IWaveSpiritAwareControl)、表单集成 (IFormItemAware)、动画开关 (IsMotionEnabled)、OnPointerPressed 事件透传 |
AtomUI.Desktop.Controls.RadioButton |
桌面端 Token 注册 (RadioButtonToken.ScopeProvider) |
实现的共享接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
IWaveSpiritAwareControl |
AtomUI.Controls.Shared |
支持点击涟漪(Wave)动画效果 |
IFormItemAware |
AtomUI.Controls.Shared |
可作为 FormItem 的子控件参与表单验证 |
RadioButtonGroup
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ItemsControl
└── AtomUI.Controls.Commons.AbstractRadioButtonGroup
├── implements IMotionAwareControl
└── implements IFormItemAware
└── AtomUI.Desktop.Controls.RadioButtonGroup
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
ItemsControl |
子项集合管理、ItemsSource / ItemTemplate 数据绑定、ItemsPanel 布局面板 |
AbstractRadioButtonGroup |
互斥选中管理 (CheckedItem)、CheckedChanged 事件、Orientation / ItemSpacing / LineSpacing 布局控制、容器生成与数据绑定适配、表单集成 |
AtomUI.Desktop.Controls.RadioButtonGroup |
桌面端 Token 注册 |
实现的共享接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
IMotionAwareControl |
AtomUI.Controls.Shared |
动画开关,向下传递给子项 |
IFormItemAware |
AtomUI.Controls.Shared |
可作为 FormItem 的子控件参与表单验证 |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 基类 | src/AtomUI.Controls/RadioButton/AbstractRadioButton.cs |
跨平台基类,定义波纹接口和表单集成 |
| 基类 | src/AtomUI.Controls/RadioButton/AbstractRadioButtonGroup.cs |
单选框组跨平台基类,定义互斥逻辑 |
| 数据模型 | src/AtomUI.Controls/RadioButton/RadioButtonOption.cs |
IRadioButtonOption 接口和默认实现 |
| 事件参数 | src/AtomUI.Controls/RadioButton/RadioButtonGroupCheckedChangedEventArgs.cs |
选中变化事件参数 |
| 指示器 | src/AtomUI.Controls/RadioButton/RadioIndicator.cs |
圆形选中指示器(内部控件,自绘制) |
| 控件类 | src/AtomUI.Desktop.Controls/RadioButton/RadioButton.cs |
桌面端 RadioButton |
| 控件类 | src/AtomUI.Desktop.Controls/RadioButton/RadioButtonGroup.cs |
桌面端 RadioButtonGroup |
| Token 定义 | src/AtomUI.Desktop.Controls/RadioButton/RadioButtonToken.cs |
组件级 Design Token |
| 主题模板 | src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioButtonTheme.axaml |
RadioButton ControlTheme |
| 主题模板 | src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioButtonGroupTheme.axaml |
RadioButtonGroup ControlTheme |
| 主题模板 | src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioIndicatorTheme.axaml |
指示器 ControlTheme |
| 主题注册 | src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioButtonThemes.axaml |
ResourceDictionary 合并入口 |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/DataEntry/RadioButtonShowCase.axaml |
使用范例 |
模板结构
RadioButton 模板
Border#Frame (Padding=0,1,0,1)
└── DockPanel (LastChildFill=True)
├── RadioIndicator#Indicator (DockPanel.Dock=Left, VerticalAlignment=Center)
│ └── Panel
│ └── WaveSpiritDecorator (PART_WaveSpirit) ← 选中时触发圆形波纹
│ └── [自绘制] 圆形外框 + 选中圆点
└── ContentPresenter#ContentPresenter (VerticalAlignment=Center)
└── 文本内容(仅当 Content 不为 null 时可见)
RadioIndicator 的自绘制逻辑:
- 使用
DrawingContext.DrawEllipse绘制外圆(边框 + 背景) - 选中时额外绘制内圆(
RadioInnerBackground),大小由RadioDotEffectSize控制 - 圆点大小通过
DoubleTransition实现缩放动画
RadioButtonGroup 模板
Border#Frame
└── ItemsPresenter#PART_ItemsPresenter
└── WrapPanel (默认 ItemsPanel)
├── RadioButton[0]
├── RadioButton[1]
├── ...
└── RadioButton[n]
布局面板特性:
- 使用
WrapPanel作为默认 ItemsPanel,支持自动换行 Orientation属性绑定到 WrapPanel,控制排列方向ItemSpacing控制项目之间的间距(默认SpacingXS)LineSpacing控制换行后的行间距(默认SpacingXS)
模板部件常量
| 部件名 | 控件类型 | 说明 |
|---|---|---|
Frame |
Border |
RadioButton / RadioButtonGroup 根框架 |
Indicator |
RadioIndicator |
圆形选中指示器 |
ContentPresenter |
ContentPresenter |
文本内容区域 |
PART_ItemsPresenter |
ItemsPresenter |
RadioButtonGroup 子项容器 |
PART_WaveSpirit |
WaveSpiritDecorator |
波纹动画装饰器(在 RadioIndicator 模板中) |