Files

221 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OptionButtonGroup 选项按钮组
## 概述
选项按钮组(OptionButtonGroup)是以按钮样式呈现的单选组件,用于在多个互斥选项中选择一个。它将传统单选按钮的逻辑与按钮的视觉形态相结合,适用于数量有限且语义明确的选项集合,如切换视图模式、选择排序方式、切换布局方向等。
AtomUI 的 `OptionButtonGroup` 控件对应 [Ant Design 5.0 Radio.Group](https://ant.design/components/radio-cn) 中 `optionType="button"` 的变体(即按钮样式的单选组),在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。
---
## 设计原理
### Ant Design 的按钮单选设计哲学
Ant Design 的 Radio.Group 在 `optionType="button"` 模式下,将每个单选选项渲染为按钮外观。与普通单选按钮(圆形勾选框)相比,按钮样式的单选组更加紧凑、视觉权重更高,适合放置在工具栏或控制面板中。
**两种按钮样式**:
| 样式 | 设计意图 | 视觉表现 |
|---|---|---|
| 🔲 **Outline(轮廓)** | 默认样式,选中项通过边框颜色和文字颜色区分 | 选中项边框变为主色(`ColorPrimary`),文字变为主色 |
| 🔳 **Solid(实色)** | 更强调选中状态,选中项有实色填充背景 | 选中项背景变为主色填充,文字变为白色 |
**三种尺寸**(通过 `SizeType` 控制):
| 尺寸 | 高度 | 说明 |
|---|---|---|
| `Large` | 40px | 大号,醒目场景 |
| `Middle` | 32px | 中号(默认),常规场景 |
| `Small` | 24px | 小号,紧凑布局 |
### Avalonia 基础能力
AtomUI 的 OptionButtonGroup 体系基于 Avalonia 的以下基础能力构建:
- **OptionButton**(`AbstractOptionButton`)继承自 `Avalonia.Controls.RadioButton`,获得单选互斥行为、`IsChecked` 状态管理、`Content` 内容呈现等能力
- **OptionButtonGroup**(`AbstractOptionButtonGroup`)继承自 `SelectingItemsControl`,获得 `ItemsSource` 数据驱动、`SelectedItem` / `SelectedIndex` 选中管理、容器化子项(`PrepareContainerForItemOverride`)等能力
### AtomUI 的实现设计
AtomUI 的 OptionButtonGroup 采用**跨平台两层继承模型**:
| 设计能力 | 实现方式 | 设计动机 |
|---|---|---|
| **两种按钮样式** | `OptionButtonStyle` 枚举 + 伪类驱动样式 | 对齐 Ant Design 的 `buttonStyle`(`solid` / `outline`) |
| **三种尺寸** | `ISizeTypeAware` 接口 + `SizeType` 共享属性 | 全局统一的 Large / Middle / Small 尺寸系统 |
| **自动位置感知** | `OptionButtonPositionTrait` 枚举 | 自动计算每个按钮的位置(First/Middle/Last/OnlyOne),裁剪圆角 |
| **图标支持** | `Icon` 属性(`PathIcon` 类型) | 按钮支持文字 + 图标组合 |
| **选中边框绘制(Outline)** | 自定义 `Render` 方法 | Outline 模式下选中项绘制主色边框覆盖 |
| **分隔线绘制** | 自定义 `Render` 方法 | 按钮之间绘制细分隔线 |
| **点击波纹** | `IWaveSpiritAwareControl` + `WaveSpiritDecorator` | 复刻 Ant Design 的 Wave 点击涟漪效果 |
| **过渡动画** | `IsMotionEnabled` + `Transitions` | 背景色/前景色/边框色平滑过渡 |
| **表单集成** | `IFormItemAware` 接口 | 可参与 FormItem 验证流程 |
| **Design Token** | `OptionButtonToken` + `RegisterTokenResourceScope` | 所有视觉值从全局/组件级 Token 派生 |
---
## 功能详解
### 按钮样式(OptionButtonStyle)
通过 `ButtonStyle` 属性设置,影响选中项的视觉表现:
- **Outline**(默认):选中项使用主色边框 + 主色文字,背景保持透明。Group 容器在选中项位置绘制一个覆盖边框
- **Solid**:选中项使用主色背景填充 + 白色文字。Solid 模式下相邻选中项之间不绘制分隔线
### 自动位置感知与圆角裁剪
OptionButtonGroup 自动为每个子按钮分配 `GroupPositionTrait`:
- `First`:第一个按钮,保留左侧圆角,右侧圆角为 0
- `Middle`:中间按钮,四个圆角均为 0
- `Last`:最后一个按钮,保留右侧圆角,左侧圆角为 0
- `OnlyOne`:唯一按钮,保留全部圆角
### 分隔线与选中边框
Group 容器的 `Render` 方法负责绘制:
1. **外部统一边框**:整个组的外围边框(`BorderBrush` + `BorderThickness`)
2. **分隔线**:每两个相邻按钮之间绘制垂直分隔线
3. **选中边框(Outline 模式)**:在选中按钮的位置绘制 `SelectedOptionBorderColor` 覆盖边框
### 数据驱动
OptionButtonGroup 支持两种使用方式:
- **声明式子项**:直接在 AXAML 中添加 `OptionButton` 子控件
- **数据绑定**:通过 `ItemsSource` 绑定 `OptionButtonData` 集合,自动生成按钮容器
---
## 与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 按钮样式单选 `optionType="button"` | ✅ | ✅ `OptionButtonGroup` + `OptionButton` | ✅ 完全对齐 |
| Outline 样式 `buttonStyle="outline"` | ✅ | ✅ `ButtonStyle="Outline"` | ✅ 完全对齐 |
| Solid 样式 `buttonStyle="solid"` | ✅ | ✅ `ButtonStyle="Solid"` | ✅ 完全对齐 |
| 三种尺寸 `size` | ✅ `large / middle / small` | ✅ `SizeType` | ✅ 完全对齐 |
| 禁用单个选项 | ✅ `disabled` | ✅ `IsEnabled` | ✅ 完全对齐 |
| 禁用整个组 | ✅ `disabled` | ✅ `IsEnabled` | ✅ 完全对齐 |
| 选中变更回调 `onChange` | ✅ 函数 | ✅ `OptionCheckedChanged` 事件 | ✅ 完全对齐 |
| 图标支持 | ✅ `icon` (5.x) | ✅ `Icon` 属性 | ✅ 完全对齐 |
| 默认选中 `defaultValue` | ✅ | ✅ `OptionButton.IsChecked="True"` | ✅ 完全对齐 |
| 数据驱动 `options` | ✅ 数组 | ✅ `ItemsSource` + `OptionButtonData` | ✅ 完全对齐 |
---
## 继承关系
### 跨平台两层模型
```
AtomUI.Controls (基础层,设备无关) AtomUI.Desktop.Controls (平台层)
────────────────────────────────── ──────────────────────────────────
AbstractOptionButton : RadioButton ───► OptionButton : AbstractOptionButton
AbstractOptionButtonGroup : SelectingItemsControl ─► OptionButtonGroup : AbstractOptionButtonGroup
```
### AbstractOptionButton(基础层)
```
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ContentControl
└── Avalonia.Controls.Primitives.ToggleButton
└── Avalonia.Controls.RadioButton
└── AtomUI.Controls.Commons.AbstractOptionButton
└── AtomUI.Desktop.Controls.OptionButton
```
### AbstractOptionButtonGroup(基础层)
```
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ItemsControl
└── Avalonia.Controls.Primitives.SelectingItemsControl
└── AtomUI.Controls.Commons.AbstractOptionButtonGroup
├── implements ISizeTypeAware
├── implements IWaveSpiritAwareControl
├── implements IFormItemAware
└── AtomUI.Desktop.Controls.OptionButtonGroup
```
**各层级职责划分:**
| 层级 | 提供的能力 |
|---|---|
| `RadioButton` | 单选互斥行为、`IsChecked` 状态、`Content` 内容、`GroupName` 分组 |
| `SelectingItemsControl` | `ItemsSource` 数据驱动、`SelectedItem` / `SelectedIndex`、容器化管理 |
| `AbstractOptionButton` | 位置感知圆角裁剪、`Icon` 图标支持、WaveSpirit 波纹、过渡动画、自定义渲染(边框绘制) |
| `AbstractOptionButtonGroup` | 按钮样式(Outline/Solid)、尺寸传播、分隔线绘制、选中边框绘制、位置分配、`OptionCheckedChanged` 事件、表单集成 |
| `OptionButton` / `OptionButtonGroup` | Desktop Token 注册(`OptionButtonToken.ScopeProvider`) |
**实现的共享接口:**
| 接口 | 定义位置 | 作用 |
|---|---|---|
| `ISizeTypeAware` | `AtomUI.Controls.Shared` | 支持 `SizeType`(Small / Middle / Large)三种尺寸切换 |
| `IWaveSpiritAwareControl` | `AtomUI.Controls.Shared` | 支持点击涟漪(Wave)动画效果 |
| `IFormItemAware` | `AtomUI.Controls.Shared` | 可作为 `FormItem` 的子控件参与表单验证 |
---
## 源码位置
### 基础层(AtomUI.Controls)
| 文件路径 | 说明 |
|---|---|
| `src/AtomUI.Controls/OptionButtonGroup/AbstractOptionButton.cs` | 设备无关的选项按钮基类 |
| `src/AtomUI.Controls/OptionButtonGroup/AbstractOptionButtonGroup.cs` | 设备无关的选项按钮组基类 |
| `src/AtomUI.Controls/OptionButtonGroup/OptionButtonGroupEnums.cs` | `OptionButtonStyle` / `OptionButtonPositionTrait` 枚举 |
| `src/AtomUI.Controls/OptionButtonGroup/OptionButtonData.cs` | 数据驱动模型 |
| `src/AtomUI.Controls/OptionButtonGroup/OptionButtonPointerEventArgs.cs` | 指针事件参数 |
| `src/AtomUI.Controls/OptionButtonGroup/OptionCheckedChangedEventArgs.cs` | 选中变更事件参数 |
### 平台层(AtomUI.Desktop.Controls)
| 文件路径 | 说明 |
|---|---|
| `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButton.cs` | 桌面端 OptionButton |
| `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButtonGroup.cs` | 桌面端 OptionButtonGroup |
| `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButtonToken.cs` | 组件级 Design Token |
| `src/AtomUI.Desktop.Controls/OptionButtonGroup/Themes/OptionButtonTheme.axaml` | OptionButton 主题 |
| `src/AtomUI.Desktop.Controls/OptionButtonGroup/Themes/OptionButtonGroupTheme.axaml` | OptionButtonGroup 主题 |
| `src/AtomUI.Desktop.Controls/OptionButtonGroup/Themes/OptionButtonBoxThemes.axaml` | 主题汇总注册 |
### Gallery 示例
| 文件路径 | 说明 |
|---|---|
| `controlgallery/AtomUIGallery/ShowCases/Views/DataEntry/RadioButtonShowCase.axaml` | RadioButton 与 OptionButton 综合范例 |
---
## 模板结构
### OptionButton 模板
```
Panel
├── WaveSpiritDecorator (PART_WaveSpirit) ← 点击波纹效果层
└── DockPanel#ContentLayout ← 内容布局
├── IconPresenter#IconPresenter ← 图标(DockPanel.Dock="Left",可选)
└── TextBlock ← 文本内容
```
### OptionButtonGroup 模板
```
Border#Frame ← 外围容器(边框+圆角)
└── ItemsPresenter (PART_ItemsPresenter) ← 子项呈现器
└── StackPanel (Horizontal) ← 默认水平排列面板
├── OptionButton ← 选项按钮 1
├── OptionButton ← 选项按钮 2
└── ...
```
**布局设计要点:**
- **自定义渲染**:OptionButtonGroup 的 `Render` 方法负责绘制外围边框、分隔线和选中边框覆盖,而非依赖模板中的多层 Border
- **圆角自动裁剪**:每个 OptionButton 根据在组中的位置自动计算圆角(仅首尾按钮保留外侧圆角)
- **分隔线绘制**:使用 `DrawLine` 在相邻按钮间绘制像素对齐的分隔线(`EdgeMode.Aliased`)