Files

17 KiB
Raw Permalink Blame History

TimePicker 时间选择器

概述

时间选择器(TimePicker)用于在弹出面板中选择或输入时间(时、分、秒)。它支持 12/24 小时制、分钟/秒钟步进、确认模式、"此刻"快捷按钮等能力,同时提供 RangeTimePicker 用于选择时间范围。

AtomUI 的 TimePicker 控件对齐了 Ant Design 5.0 TimePicker 的设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。


设计原理

Ant Design 的时间选择器设计哲学

Ant Design 的 TimePicker 定位是:「当用户需要输入一个时间,可以点击标准输入框,弹出时间面板进行选择」。它提供了以下核心能力:

  • 12/24 小时制:根据业务场景切换时钟格式。
  • 步进选择:通过 minuteStep(分钟步进)和 secondStep(秒钟步进)控制候选项粒度。
  • 时间范围选择:通过 RangePicker 模式支持选择起止时间。
  • 确认模式:可选"确认"按钮,避免误操作。
  • "此刻"快捷按钮:一键填充当前时间。

与 Avalonia TimePicker 的关系

AtomUI 的 TimePicker 没有继承 Avalonia 自带的 Avalonia.Controls.TimePicker。Avalonia 原生 TimePicker 使用固定的三列滚轮交互模式,功能较为基础。AtomUI 基于自有的 InfoPickerInput 基础设施从头构建,提供更丰富的功能和更贴近 Ant Design 的交互体验。

AtomUI 的扩展设计

AtomUI TimePicker 基于 InfoPickerInput(信息选择器输入框)基类构建,该基类封装了弹出面板(Flyout)管理、输入框交互、清除按钮、装饰框(AddOnDecoratedBox)等通用逻辑。在此基础上 TimePicker 扩展了以下能力:

扩展能力 实现方式 设计动机
12/24 小时制 ClockIdentifierType 枚举 对齐 Ant Design 的 use12Hours 属性
分钟/秒钟步进 MinuteIncrement / SecondIncrement 属性 对齐 Ant Design 的 minuteStep / secondStep
确认模式 IsNeedConfirm 属性 对齐 Ant Design 的 needConfirm,需要点击确认按钮才应用选择
"此刻"按钮 IsShowNow 属性 对齐 Ant Design 的 showNow,一键设置当前时间
默认值 DefaultTime 属性 控件加载时自动填充默认时间
时间范围选择 RangeTimePicker 独立控件 对齐 Ant Design 的 TimePicker.RangePicker
三种尺寸 ISizeTypeAware 接口 + SizeType 共享属性 全局统一的 Large / Middle / Small 尺寸系统
三种样式变体 IInputControlStyleVariantAware + StyleVariant Outline / Filled / Borderless 三种输入框风格
验证状态 IInputControlStatusAware + Status Default / Warning / Error 三种状态反馈
弹出面板 TimePickerFlyout + TimePickerPresenter + TimeView 内部组件协作管理弹出面板的渲染和交互
国际化 LanguageProvider 系统 AM/PM 文本和"此刻"按钮文案可本地化
表单集成 IFormItemAware 接口 可参与 FormItem 验证流程
紧凑空间 ICompactSpaceAware 接口 在 Space.Compact 容器中自动调整圆角和边框

功能详解

时钟格式(ClockIdentifier)

通过 ClockIdentifier 属性设置时钟格式:

格式 枚举值 显示格式 面板列数
24 小时制 HourClock24(默认) HH:mm:ss(如 14:30:00) 3 列(时/分/秒)
12 小时制 HourClock12 hh:mm:ss AM/PM(如 02:30:00 PM) 4 列(时/分/秒/上下午)

12 小时制下,面板会额外显示 AM/PM(上午/下午)选择列。AM/PM 的显示文本通过国际化系统提供(中文为「上午」「下午」,英文为「AM」「PM」)。

步进选择(MinuteIncrement / SecondIncrement)

  • MinuteIncrement:分钟列的步进值,有效范围 1–59,默认 1。
  • SecondIncrement:秒钟列的步进值,有效范围 1–59,默认 1。

例如设置 MinuteIncrement="15" 后,分钟列仅显示 00、15、30、45 四个选项。

确认模式(IsNeedConfirm)

当 IsNeedConfirm = true 时:

  1. 面板底部显示「确定」按钮
  2. 用户滚动选择时间后,需要点击确认才会将值应用到输入框
  3. 在确认前,面板顶部会实时预览 hover 中的时间值

当 IsNeedConfirm = false(默认)时:

  1. 用户双击某个选项或使用"此刻"按钮时直接关闭面板并应用值
  2. 关闭面板时自动应用当前选中的时间

"此刻"按钮(IsShowNow)

当 IsShowNow = true(默认)时,面板底部显示"此刻"/"Now" 快捷按钮。点击后:

  • 立即将 SelectedTime 设为当前时间(DateTime.Now.TimeOfDay)
  • 如果不在确认模式下,同时关闭面板

默认值(DefaultTime)

控件首次加载(OnAttachedToLogicalTree)时,如果 SelectedTime 为 null 且 DefaultTime 不为 null,则自动将 SelectedTime 设为 DefaultTime。

调用 Reset() 方法可将时间重置为 DefaultTime。调用 Clear() 方法则将时间设为 null,忽略默认值。

时间范围选择(RangeTimePicker)

RangeTimePicker 继承自 RangeInfoPickerInput,提供两个输入框(起始时间 / 结束时间),共享同一个弹出面板。

交互流程:

  1. 点击起始输入框,面板打开,选择起始时间
  2. 如果结束时间为空,自动切换到结束时间输入框
  3. 两个时间都选定后关闭面板
  4. 支持通过 RangeStartDefaultTime / RangeEndDefaultTime 设置默认值

与 Ant Design 规范的对齐程度

特性 Ant Design (React) AtomUI (Avalonia) 对齐情况
基本选择 ✅ 点击弹出面板 ✅ TimePickerFlyout ✅ 完全对齐
12/24 小时制 ✅ use12Hours ✅ ClockIdentifier 枚举 ✅ 完全对齐
分钟步进 ✅ minuteStep ✅ MinuteIncrement ✅ 完全对齐
秒钟步进 ✅ secondStep ✅ SecondIncrement ✅ 完全对齐
确认模式 ✅ needConfirm ✅ IsNeedConfirm ✅ 完全对齐
"此刻"按钮 ✅ showNow ✅ IsShowNow ✅ 完全对齐
三种尺寸 ✅ size ✅ SizeType 属性 ✅ 完全对齐
样式变体 ✅ variant ✅ StyleVariant(Outline/Filled/Borderless) ✅ 完全对齐
状态反馈 ✅ status ✅ Status(Default/Warning/Error) ✅ 完全对齐
范围选择 ✅ RangePicker ✅ RangeTimePicker ✅ 完全对齐
禁用 ✅ disabled ✅ IsEnabled=False ✅ 完全对齐
占位文本 ✅ placeholder ✅ PlaceholderText ✅ 完全对齐
清除按钮 ✅ allowClear ✅ 内置 PickerClearUpButton ✅ 完全对齐
自定义图标 ✅ suffixIcon ✅ InfoIcon(默认 ClockCircleOutlined) ✅ 完全对齐
禁用指定时间 ✅ disabledTime ❌ 暂未支持 ⚠️ 待实现
自定义格式 ✅ format ❌ 暂未支持自定义格式 ⚠️ 内部使用固定格式
仅时/仅时分 ✅ 通过 format 控制 ❌ 始终显示时/分/秒 ⚠️ 待支持
附加内容 renderExtraFooter ✅ ❌ 暂未支持 ⚠️ 待实现
open 受控显示 ✅ ❌ 暂未支持 ⚠️ 待实现

继承关系

TimePicker

Avalonia.Controls.Primitives.TemplatedControl
  └── AtomUI.Desktop.Controls.Primitives.InfoPickerInput
        ├── implements IMotionAwareControl
        ├── implements ICompactSpaceAware
        ├── implements IFormItemAware
        ├── implements IInputControlStatusAware
        ├── implements IInputControlStyleVariantAware
        ├── implements ISizeTypeAware
        └── AtomUI.Desktop.Controls.TimePicker

RangeTimePicker

Avalonia.Controls.Primitives.TemplatedControl
  └── AtomUI.Desktop.Controls.Primitives.InfoPickerInput
        └── AtomUI.Desktop.Controls.Primitives.RangeInfoPickerInput
              └── AtomUI.Desktop.Controls.RangeTimePicker

各层级职责划分:

层级 提供的能力
TemplatedControl 模板化控件基础设施
InfoPickerInput 弹出面板(Flyout)管理、输入框交互、清除按钮、装饰框(AddOnDecoratedBox)、尺寸/样式变体/状态、表单集成、紧凑空间、Motion 控制
RangeInfoPickerInput 双输入框(起始/结束)、范围激活部分追踪、选中指示器动画
TimePicker 时间特有属性(ClockIdentifier、MinuteIncrement、SelectedTime 等)、TimePickerFlyout 创建、时间格式化显示
RangeTimePicker 范围时间特有属性(RangeStartSelectedTime / RangeEndSelectedTime 等)、范围交互流程

实现的共享接口:

接口 定义位置 作用
ISizeTypeAware AtomUI.Controls.Shared 支持 SizeType(Small / Middle / Large)三种尺寸切换
IInputControlStyleVariantAware AtomUI.Controls.Shared 支持 StyleVariant(Outline / Filled / Borderless)三种样式变体
IInputControlStatusAware AtomUI.Controls.Shared 支持 Status(Default / Warning / Error)三种验证状态
IMotionAwareControl AtomUI.Controls.Shared 支持 IsMotionEnabled 控制过渡动画开关
ICompactSpaceAware AtomUI.Controls.Shared 在 Space.Compact 中使用时自动调整圆角和边框
IFormItemAware AtomUI.Controls.Shared 可作为 FormItem 的子控件参与表单验证

内部组件结构

TimePicker 的弹出面板由多个内部组件协作完成:

TimePicker / RangeTimePicker
  └── TimePickerFlyout (内部, Flyout 子类)
        └── FlyoutPresenter
              └── TimePickerPresenter (内部, PickerPresenterBase 子类)
                    ├── TimeView (内部, TemplatedControl)
                    │   ├── DateTimePickerPanel (小时列)
                    │   ├── DateTimePickerPanel (分钟列)
                    │   ├── DateTimePickerPanel (秒钟列)
                    │   └── DateTimePickerPanel (AM/PM 列, 12小时制时显示)
                    ├── Button ("此刻"按钮)
                    └── Button ("确定"按钮, 确认模式时显示)
组件 可见性 职责
TimePickerFlyout internal 管理 Flyout 生命周期,创建 TimePickerPresenter,透传属性绑定
TimePickerPresenter internal 组装 TimeView 与操作按钮,处理键盘事件,管理确认/取消逻辑
TimeView internal 管理时/分/秒/AM-PM 四个 DateTimePickerPanel 的数据和交互
DateTimePickerPanel internal 单列滚轮选择器,支持循环滚动、选中高亮、hover 事件
TimeViewCell internal 滚轮中每个时间单元格的视觉表示

源码位置

层级 文件路径 说明
基类 src/AtomUI.Desktop.Controls/Primitives/InfoPickerInput/InfoPickerInput.cs 信息选择器输入框基类
基类 src/AtomUI.Desktop.Controls/Primitives/InfoPickerInput/RangeInfoPickerInput.cs 范围信息选择器输入框基类
控件类 src/AtomUI.Desktop.Controls/TimePicker/TimePicker.cs TimePicker 实现
控件类 src/AtomUI.Desktop.Controls/TimePicker/RangeTimePicker.cs RangeTimePicker 实现
弹出面板 src/AtomUI.Desktop.Controls/TimePicker/TimePickerFlyout.cs Flyout 适配
面板呈现 src/AtomUI.Desktop.Controls/TimePicker/TimePickerPresenter.cs 面板呈现器(按钮 + TimeView)
时间视图 src/AtomUI.Desktop.Controls/TimePicker/TimeView/TimeView.cs 时/分/秒滚轮面板
时间单元格 src/AtomUI.Desktop.Controls/TimePicker/TimeView/TimeViewCell.cs 时间单元格渲染
滚轮面板 src/AtomUI.Desktop.Controls/TimePicker/TimeView/DateTimePickerPanel.cs 单列滚轮选择器
Token 定义 src/AtomUI.Desktop.Controls/TimePicker/TimePickerToken.cs 组件级 Design Token
主题模板 src/AtomUI.Desktop.Controls/TimePicker/Themes/TimePickerTheme.axaml TimePicker ControlTheme
主题模板 src/AtomUI.Desktop.Controls/TimePicker/Themes/RangeTimePickerTheme.axaml RangeTimePicker ControlTheme
主题模板 src/AtomUI.Desktop.Controls/TimePicker/Themes/TimePickerPresenterTheme.axaml 面板呈现器 ControlTheme
主题模板 src/AtomUI.Desktop.Controls/TimePicker/Themes/TimeViewTheme.axaml TimeView ControlTheme
主题常量 src/AtomUI.Desktop.Controls/TimePicker/Themes/TimePickerThemeConstants.cs 模板部件名称常量
国际化 src/AtomUI.Desktop.Controls/TimePicker/Localization/en_US.cs 英文语言资源
国际化 src/AtomUI.Desktop.Controls/TimePicker/Localization/zh_CN.cs 中文语言资源
Gallery 示例 controlgallery/AtomUIGallery/ShowCases/Views/DataEntry/TimePickerShowCase.axaml 使用范例

模板结构

TimePicker 模板

TimePicker 的 ControlTheme 基于 InfoPickerInputTheme,整体模板结构继承自 InfoPickerInput:

AddOnDecoratedBox (PART_AddOnDecoratedBox)
├── [可选] LeftAddOn                           ← 左侧附加内容
├── Border#ContentFrame (PART_PickerInnerBox)   ← 主输入区域
│   ├── TextBox (PART_InfoInputBox)             ← 时间文本显示/输入
│   └── PickerClearUpButton                     ← 清除按钮 + 图标按钮区域
│       ├── ClockCircleOutlined                 ← 默认图标(时钟图标)
│       └── InputClearIconButton                ← 清除按钮(hover 时显示)
└── [可选] RightAddOn                           ← 右侧附加内容

RangeTimePicker 模板

RangeTimePicker 基于 RangeInfoPickerInputTheme,增加了双输入框和范围指示器:

AddOnDecoratedBox (PART_AddOnDecoratedBox)
├── Border#ContentFrame
│   ├── TextBox (PART_InfoInputBox)              ← 起始时间输入
│   ├── PathIcon (PART_RangePickerArrow)         ← 范围箭头 (→)
│   ├── TextBox (PART_SecondaryInfoInputBox)     ← 结束时间输入
│   ├── PickerClearUpButton                      ← 清除/图标按钮区域
│   └── Rectangle (PART_RangePickerIndicator)    ← 激活部分下划线指示器

TimePickerPresenter 模板

弹出面板的内部结构:

DockPanel (PART_MainLayout)
├── Border#PART_ButtonsFrame (DockPanel.Dock="Bottom")  ← 按钮区域框架
│   └── Panel (PART_ButtonsLayout)
│       ├── Button (PART_NowButton)              ← "此刻"快捷按钮 (Link 类型)
│       └── Button (PART_ConfirmButton)          ← "确定"按钮 (Primary 类型)
└── TimeView (PART_TimeView)                     ← 时间滚轮视图(填充剩余空间)
    ├── DateTimePickerPanel (PART_HourSelector)   ← 小时列
    ├── Rectangle (PART_FirstSpacer)              ← 分隔线
    ├── DateTimePickerPanel (PART_MinuteSelector) ← 分钟列
    ├── Rectangle (PART_SecondSpacer)             ← 分隔线
    ├── DateTimePickerPanel (PART_SecondSelector) ← 秒钟列
    ├── Rectangle (PART_ThirdSpacer)              ← 分隔线(12小时制时显示)
    └── Panel (PART_PeriodHost)                   ← AM/PM 选择列(12小时制时显示)
        └── DateTimePickerPanel (PART_PeriodSelector)

模板部件常量

TimePickerPresenterThemeConstants

常量名 值 说明
MainLayoutPart "PART_MainLayout" 主布局面板
NowButtonPart "PART_NowButton" "此刻"快捷按钮
ConfirmButtonPart "PART_ConfirmButton" "确定"按钮
ButtonsLayoutPart "PART_ButtonsLayout" 按钮布局面板
ButtonsFramePart "PART_ButtonsFrame" 按钮区域框架
TimeViewPart "PART_TimeView" 时间滚轮视图

TimeViewThemeConstants

常量名 值 说明
RootLayoutPart "PART_RootLayout" 根布局
MainFramePart "PART_MainFrame" 主框架
PickerSelectorContainerPart "PART_PickerContainer" 选择器容器
HeaderTextPart "PART_HeaderText" 头部文本
HourSelectorPart "PART_HourSelector" 小时选择器
MinuteSelectorPart "PART_MinuteSelector" 分钟选择器
SecondSelectorPart "PART_SecondSelector" 秒钟选择器
PeriodSelectorPart "PART_PeriodSelector" AM/PM 选择器
PeriodHostPart "PART_PeriodHost" AM/PM 容器
FirstSpacerPart "PART_FirstSpacer" 第一条分隔线
SecondSpacerPart "PART_SecondSpacer" 第二条分隔线
ThirdSpacerPart "PART_ThirdSpacer" 第三条分隔线