16 KiB
DatePicker 日期选择器
概述
日期选择器(DatePicker)用于选择日期或日期范围。用户通过点击输入框弹出日历面板,在面板中选择日期。它是表单场景中最常用的日期输入控件,广泛应用于预约、筛选、时间范围设定等业务场景。
AtomUI 的 DatePicker 和 RangeDatePicker 控件完整复刻了 Ant Design 5.0 DatePicker 的设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。
设计原理
Ant Design 的日期选择器设计哲学
Ant Design 对日期选择器的定位是:「当用户需要输入一个日期,可以点击标准输入框,弹出日期面板进行选择」。为了覆盖不同的日期选择场景,Ant Design 建立了一套完整的日期选择器体系:
两种选择器模式:
| 模式 | 设计意图 | 典型用途 |
|---|---|---|
| 📅 DatePicker(单日期选择) | 选择单个日期或日期时间,是最基础的日期输入方式 | 「出生日期」「发布时间」「截止日期」 |
| 📅↔📅 RangeDatePicker(范围日期选择) | 同时选择起止日期,通过双日历面板直观展示日期范围 | 「入住/退房日期」「统计周期」「请假起止时间」 |
关键交互特性:
| 特性 | 设计意图 |
|---|---|
| 📆 日历面板弹出 | 点击输入框弹出浮层日历面板,选择后自动回填 |
| ⏰ 日期+时间组合 | 通过 IsShowTime 开启时间选择,在日历面板旁附加时间选择器 |
| ✅ 确认按钮 | 通过 IsNeedConfirm 控制是否需要手动确认,防止误选 |
| 📍 今天/现在快捷按钮 | 快速定位到当前日期或当前时刻 |
| 🚫 禁用状态 | 灰色调 + 不可交互,适用于权限不足或条件不满足的场景 |
| 🧹 一键清除 | 悬浮时显示清除按钮,快速清空已选值 |
Avalonia 基础能力
AtomUI 的 DatePicker 并不继承自 Avalonia 的内置 Avalonia.Controls.DatePicker,而是基于 AtomUI 自研的 InfoPickerInput 基础架构构建。InfoPickerInput 是一个通用的"信息选择器输入框"抽象基类,提供了输入框 + Flyout 弹出面板的标准交互模式。
InfoPickerInput 提供的核心能力:
| 能力 | 说明 |
|---|---|
| 输入框外观 | 标准化的输入框容器,支持前后缀(AddOn)、图标、占位文本 |
| Flyout 弹出 | 点击输入框自动弹出选择面板,支持多种弹出方向 |
| 清除按钮 | 悬浮时显示清除图标,一键清空 |
| 尺寸系统 | 支持 SizeType(Large / Middle / Small)三种尺寸 |
| 样式变体 | 支持 StyleVariant(Outline / Filled / Borderless)三种外观 |
| 状态指示 | 支持 Status(Default / Warning / Error)验证状态 |
| 表单集成 | 实现 IFormItemAware,可参与表单验证 |
| 紧凑空间 | 实现 ICompactSpaceAware,在 Space.Compact 中自动调整 |
AtomUI 的扩展设计
AtomUI DatePicker 在 InfoPickerInput 的基础上,遵循 Ant Design 5.0 规范做了以下扩展:
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| 单日期选择 | DatePicker 控件 + DatePickerPresenter |
最基础的日期选择场景 |
| 范围日期选择 | RangeDatePicker 控件 + 双日历面板 |
对齐 Ant Design 的 RangePicker |
| 日期+时间选择 | IsShowTime 属性 + TimeView 面板 |
选择精确到时分秒的时间点 |
| 确认模式 | IsNeedConfirm 属性 + 确认按钮 |
防止误选,需手动确认 |
| 今天/现在快捷 | IsShowNow 属性 + 快捷按钮 |
快速定位到当前时间 |
| 12/24小时制 | ClockIdentifier 属性 |
支持 12 小时制(AM/PM)和 24 小时制 |
| 自定义格式 | Format 属性 |
自定义日期显示格式 |
| 默认值支持 | DefaultDateTime 属性 |
初始化时自动设置默认日期 |
| 三种尺寸 | ISizeTypeAware 接口 + SizeType |
全局统一的 Large / Middle / Small 尺寸系统 |
| 三种样式变体 | IInputControlStyleVariantAware 接口 |
Outline / Filled / Borderless 外观样式 |
| 验证状态 | IInputControlStatusAware 接口 |
Default / Warning / Error 状态反馈 |
| Design Token | DatePickerToken + RegisterTokenResourceScope |
所有视觉值从全局/组件级 Token 派生 |
| 国际化 | LanguageProvider 系统 |
「今天」「现在」按钮文本支持 i18n |
功能详解
单日期选择(DatePicker)
DatePicker 是最基础的日期选择控件。点击输入框弹出日历面板,用户在面板中选择日期后自动回填到输入框中。
默认行为:
- 选择日期后立即确认(无需点击确认按钮),Flyout 自动关闭
- 显示格式为
yyyy-MM-dd - 左侧显示日历图标(
CalendarOutlined)
范围日期选择(RangeDatePicker)
RangeDatePicker 用于选择一个日期范围(起始日期 ~ 结束日期)。它继承自 RangeInfoPickerInput,提供双输入框 + 箭头分隔符的标准范围选择器外观。
默认行为:
- 点击左侧输入框选择起始日期,选择后自动切换到右侧输入框选择结束日期
- 不显示时间时,使用双月日历面板(
DualMonthRangeCalendar),一次展示两个月的日期 - 显示时间时,使用单日历面板 + 时间选择器(
TimedRangeDatePickerPresenter) - 清除时,起始和结束日期同时清空
日期+时间选择(IsShowTime)
当 IsShowTime = true 时:
- 日历面板右侧附加
TimeView时间选择器 - 显示格式自动扩展为
yyyy-MM-dd HH:mm:ss(24小时制)或yyyy-MM-dd hh:mm:ss tt(12小时制) - 自动启用确认模式(
IsNeedConfirm被强制设为true) - 底部显示「现在」快捷按钮(替代「今天」按钮)
确认模式(IsNeedConfirm)
当 IsNeedConfirm = true 时:
- 底部操作栏显示「确定」按钮
- 选择日期后不会立即关闭 Flyout,需点击确认按钮才会提交
- 当尚未选择日期时,确认按钮为禁用状态
今天/现在快捷按钮(IsShowNow)
当 IsShowNow = true(默认值)时:
- 非时间模式:显示「今天」链接按钮,点击后选择当天日期
- 时间模式:显示「现在」链接按钮,点击后选择当前日期和时间
自定义格式(Format)
通过 Format 属性可以自定义日期显示格式,支持标准的 .NET DateTime 格式字符串。如果未设置,将根据 IsShowTime 和 ClockIdentifier 自动确定默认格式。
弹出方向(PickerPlacement)
通过 PickerPlacement 属性控制日历面板的弹出方向,支持 TopLeft、TopRight、BottomLeft(默认)、BottomRight 四个方向。
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
日期选择 DatePicker |
✅ | ✅ DatePicker 控件 |
✅ 完全对齐 |
范围选择 RangePicker |
✅ | ✅ RangeDatePicker 控件 |
✅ 完全对齐 |
日期+时间 showTime |
✅ 布尔/对象 | ✅ IsShowTime 布尔 |
⚠️ 不支持 TimePicker 配置对象 |
确认按钮 needConfirm |
✅ | ✅ IsNeedConfirm |
✅ 完全对齐 |
今天按钮 showNow |
✅ | ✅ IsShowNow |
✅ 完全对齐 |
自定义格式 format |
✅ | ✅ Format 属性 |
✅ 完全对齐 |
禁用 disabled |
✅ | ✅ IsEnabled |
✅ 完全对齐 |
清除 allowClear |
✅ | ✅ 内置清除按钮 | ✅ 完全对齐 |
三种尺寸 size |
✅ large / middle / small |
✅ SizeType 属性 |
✅ 完全对齐 |
样式变体 variant |
✅ outlined / filled / borderless |
✅ StyleVariant 属性 |
✅ 完全对齐 |
状态 status |
✅ warning / error |
✅ Status 属性 |
✅ 完全对齐 |
弹出方向 placement |
✅ | ✅ PickerPlacement 属性 |
✅ 完全对齐 |
| 12/24小时制 | ✅ use12Hours |
✅ ClockIdentifier 属性 |
✅ 完全对齐 |
| 国际化 | ✅ locale |
✅ LanguageProvider 系统 |
✅ 完全对齐 |
默认值 defaultValue |
✅ | ✅ DefaultDateTime / RangeStartDefaultDate |
✅ 完全对齐 |
占位文本 placeholder |
✅ | ✅ PlaceholderText |
✅ 完全对齐 |
日期面板 picker |
✅ date / week / month / quarter / year |
⚠️ 仅支持 date 模式 |
⚠️ 待扩展 |
预设范围 presets |
✅ | ❌ 暂未支持 | ⚠️ 待支持 |
禁用日期 disabledDate |
✅ 回调函数 | ❌ 暂未支持 | ⚠️ 待支持 |
额外页脚 renderExtraFooter |
✅ | ❌ 暂未支持 | ⚠️ 待支持 |
继承关系
DatePicker
Avalonia.Controls.Primitives.TemplatedControl
└── AtomUI.Desktop.Controls.Primitives.InfoPickerInput
├── implements IMotionAwareControl
├── implements ICompactSpaceAware
├── implements IFormItemAware
├── implements IInputControlStatusAware
├── implements IInputControlStyleVariantAware
├── implements ISizeTypeAware
└── implements IFormItemFeedbackAware
└── AtomUI.Desktop.Controls.DatePicker
RangeDatePicker
Avalonia.Controls.Primitives.TemplatedControl
└── AtomUI.Desktop.Controls.Primitives.InfoPickerInput
└── AtomUI.Desktop.Controls.Primitives.RangeInfoPickerInput
└── AtomUI.Desktop.Controls.RangeDatePicker
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
TemplatedControl |
模板化控件基础设施、样式系统 |
InfoPickerInput |
输入框 + Flyout 弹出面板标准交互模式、清除按钮、尺寸系统、样式变体、验证状态、AddOn 前后缀、表单集成、紧凑空间适配 |
RangeInfoPickerInput |
双输入框布局、范围激活部分管理、选择指示器动画、起止输入框切换逻辑 |
DatePicker |
单日期选择逻辑、日历面板创建与绑定、日期格式化、默认值管理 |
RangeDatePicker |
范围日期选择逻辑、双月日历/单月+时间面板切换、起止日期管理与自动修正 |
实现的共享接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
ISizeTypeAware |
AtomUI.Controls.Shared |
支持 SizeType(Small / Middle / Large)三种尺寸 |
IMotionAwareControl |
AtomUI.Controls.Shared |
支持过渡动画开关 |
ICompactSpaceAware |
AtomUI.Controls.Shared |
在 Space.Compact 中使用时自动调整 |
IFormItemAware |
AtomUI.Controls.Shared |
可参与 FormItem 表单验证 |
IInputControlStatusAware |
AtomUI.Controls.Shared |
支持验证状态(Warning / Error) |
IInputControlStyleVariantAware |
AtomUI.Controls.Shared |
支持样式变体(Outline / Filled / Borderless) |
IFormItemFeedbackAware |
AtomUI.Controls.Shared |
支持表单验证反馈展示 |
内部组件体系
DatePicker 的实现涉及多个内部组件协同工作:
| 组件 | 可见性 | 职责 |
|---|---|---|
DatePicker |
public |
单日期选择器主控件 |
RangeDatePicker |
public |
范围日期选择器主控件 |
DatePickerFlyout |
internal |
单日期选择弹出层,创建并管理 DatePickerPresenter |
RangeDatePickerFlyout |
internal |
范围日期选择弹出层,根据 IsShowTime 动态切换 Presenter 类型 |
DatePickerPresenter |
internal |
单日期选择面板,包含日历视图 + 操作按钮 |
RangeDatePickerPresenter |
internal |
范围日期选择面板基类,支持分步选择起止日期 |
DualMonthRangeDatePickerPresenter |
internal |
双月日历范围选择面板(无时间选择时使用) |
TimedRangeDatePickerPresenter |
internal |
含时间选择的范围选择面板(有时间选择时使用) |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 控件类 | src/AtomUI.Desktop.Controls/DatePicker/DatePicker.cs |
单日期选择器 |
| 控件类 | src/AtomUI.Desktop.Controls/DatePicker/RangeDatePicker.cs |
范围日期选择器 |
| Flyout | src/AtomUI.Desktop.Controls/DatePicker/DatePickerFlyout.cs |
单日期弹出层 |
| Flyout | src/AtomUI.Desktop.Controls/DatePicker/RangeDatePickerFlyout.cs |
范围日期弹出层 |
| Presenter | src/AtomUI.Desktop.Controls/DatePicker/DatePickerPresenter.cs |
单日期选择面板 |
| Presenter | src/AtomUI.Desktop.Controls/DatePicker/RangeDatePickerPresenter.cs |
范围日期选择面板基类 |
| Presenter | src/AtomUI.Desktop.Controls/DatePicker/DualMonthRangeDatePickerPresenter.cs |
双月日历范围选择面板 |
| Presenter | src/AtomUI.Desktop.Controls/DatePicker/TimedRangeDatePickerPresenter.cs |
含时间的范围选择面板 |
| Token 定义 | src/AtomUI.Desktop.Controls/DatePicker/DatePickerToken.cs |
组件级 Design Token |
| 国际化 | src/AtomUI.Desktop.Controls/DatePicker/Localization/en_US.cs |
英文语言资源 |
| 国际化 | src/AtomUI.Desktop.Controls/DatePicker/Localization/zh_CN.cs |
中文语言资源 |
| 主题模板 | src/AtomUI.Desktop.Controls/DatePicker/Themes/DatePickerTheme.axaml |
DatePicker 主题(基于 InfoPickerInputTheme) |
| 主题模板 | src/AtomUI.Desktop.Controls/DatePicker/Themes/RangeDatePickerTheme.axaml |
RangeDatePicker 主题(基于 RangeInfoPickerInputTheme) |
| 主题模板 | src/AtomUI.Desktop.Controls/DatePicker/Themes/DatePickerPresenterTheme.axaml |
Presenter 主题模板 |
| 主题模板 | src/AtomUI.Desktop.Controls/DatePicker/Themes/DualMonthRangeDatePickerPresenterTheme.axaml |
双月范围 Presenter 主题 |
| 主题模板 | src/AtomUI.Desktop.Controls/DatePicker/Themes/TimedRangeDatePickerPresenterTheme.axaml |
含时间范围 Presenter 主题 |
| 主题常量 | src/AtomUI.Desktop.Controls/DatePicker/Themes/DatePickerThemeConstants.cs |
模板部件名称常量 |
| 主题注册 | src/AtomUI.Desktop.Controls/DatePicker/Themes/DatePickerThemes.axaml |
主题资源注册 |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/DataEntry/DatePickerShowCase.axaml |
使用范例 |
模板结构
DatePicker 模板
DatePicker 的 ControlTheme 基于 InfoPickerInputTheme,即标准的信息选择器输入框模板。其核心结构为:
InfoPickerInput 标准布局
├── AddOnDecoratedBox (外框 + 前后缀)
│ ├── ContentLeftAddOn (左侧内容附加区)
│ ├── PickerInnerBox (输入框区域)
│ │ ├── InfoIcon (CalendarOutlined 日历图标)
│ │ ├── TextBox (日期文本输入框)
│ │ └── ClearButton (清除按钮,悬浮时显示)
│ └── ContentRightAddOn (右侧内容附加区)
└── Flyout → DatePickerPresenter
├── Calendar (日历视图)
├── TimeView (时间选择器,IsShowTime=true 时显示)
└── ButtonsPanel (操作按钮栏)
├── TodayButton / NowButton (今天/现在快捷按钮)
└── ConfirmButton (确认按钮)
RangeDatePicker 模板
RangeDatePicker 基于 RangeInfoPickerInputTheme,提供双输入框布局:
RangeInfoPickerInput 标准布局
├── AddOnDecoratedBox (外框)
│ ├── InfoInputBox (起始日期输入框)
│ ├── RangePickerArrow (↔ 箭头分隔符)
│ ├── SecondaryInfoInputBox (结束日期输入框)
│ ├── InfoIcon (CalendarOutlined)
│ ├── ClearButton (清除按钮)
│ └── RangePickerIndicator (选择指示条)
└── Flyout → DualMonthRangeDatePickerPresenter 或 TimedRangeDatePickerPresenter
├── DualMonthRangeCalendar / RangeCalendar (双月/单月日历)
├── TimeView (时间选择器,IsShowTime=true 时显示)
└── ButtonsPanel (操作按钮栏)
Presenter 模板部件常量
| 常量名 | 值 | 说明 |
|---|---|---|
DatePickerPresenterThemeConstants.NowButtonPart |
"PART_NowButton" |
「现在」快捷按钮 |
DatePickerPresenterThemeConstants.TodayButtonPart |
"PART_TodayButton" |
「今天」快捷按钮 |
DatePickerPresenterThemeConstants.ConfirmButtonPart |
"PART_ConfirmButton" |
确认按钮 |
DatePickerPresenterThemeConstants.CalendarViewPart |
"PART_CalendarView" |
日历视图 |
DatePickerPresenterThemeConstants.TimeViewPart |
"PART_TimeView" |
时间选择器视图 |