- UI组件
- 前端
【免费下载链接】ant-design-blazor
🌈A rich set of enterprise-class UI components based on Ant Design and Blazor.
导读
本文围绕 ant-design-blazor 仓库中的 Calendar 组件文档 展开,系统讲解日历组件的适用场景、全部 API 参数、事件回调与国际化注意事项,并结合仓库源码与官方示例深入剖析dateCellRender、monthCellRender、headerRender等高级自定义能力的实现原理。读完本文,你将掌握如何用<Calendar>组件快速搭建日程、课表、价格日历等按日期划分的数据展示容器,并能按需定制日期单元格内容、切换年/月面板甚至完全重写日历头部。
何时使用 Calendar 组件
Calendar是"按照日历形式展示数据的容器"。当业务数据本身是日期、或者天然按照日期划分时,就适合使用它,典型场景包括:
- 日程/事件列表:每天展示若干条提醒或事件;
- 课表:按日期维度呈现课程安排;
- 价格日历:在日期格内展示当天价格;
- 农历等历法信息:在日期格内追加农历等附加数据。
组件目前支持年/月两种面板模式切换,配合自定义渲染函数即可在单元格内叠加任意内容。组件类定义位于 components/calendar/Calendar.razor.cs,其文档注释与官方文档保持一致:"Container for displaying data in calendar form."。
基本用法:一个开箱即用的日历面板
最简单的用法是直接声明<Calendar />,即可得到一个支持年/月切换的通用日历面板。官方示例 Basic.razor 展示了绑定面板切换事件的基本写法:
<Calendar OnPanelChange="OnPanelChange" /> @code { private void OnPanelChange(DateTime value, DatePickerType type) { Console.WriteLine($"{value.ToString("yyyy-MM-dd")} {type}"); } }OnPanelChange在用户切换年/月视图(即面板变化)时触发,回调参数为当前面板代表日期(DateTime)与面板类型(DatePickerType)。注意DatePickerType定义在 components/date-picker/types 目录中,它同时被日期选择器与日历组件共用,用于标识日期粒度(如Date、Month)。
API 参数全解
以下参数表完整继承自官方文档 index.zh-CN.md,并结合源码实现逐项补充了默认值与行为细节:
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| dateCellRender | 自定义渲染日期单元格,返回内容会被追加到单元格 | Func<DateTime, RenderFragment> | 无 |
| dateFullCellRender | 自定义渲染日期单元格,返回内容覆盖单元格 | Func<DateTime, RenderFragment> | 无 |
| defaultValue | 默认展示的日期 | DateTime | 默认日期 |
| disabledDate | 不可选择的日期 | Func<DateTime, bool> | 无 |
| fullscreen | 是否全屏显示 | bool | true |
| locale | 国际化配置(TODO) | DatePickerLocale | 全局 Locale 的 DatePicker 配置 |
| mode | 初始模式,CalendarMode.Month/CalendarMode.Year | CalendarMode | CalendarMode.Month |
| monthCellRender | 自定义渲染月单元格,返回内容会被追加到单元格 | Func<DateTime, RenderFragment> | 无 |
| monthFullCellRender | 自定义渲染月单元格,返回内容覆盖单元格 | Func<DateTime, RenderFragment> | 无 |
| validRange | 设置可以显示的日期范围 | DateTime[](两个元素) | 无 |
| value | 展示日期 | DateTime | 当前日期 |
| onPanelChange | 日期面板变化回调 | Action<DateTime, DatePickerType> | 无 |
| onSelect | 点击选择日期回调(已标记 Obsolete,建议改用 onChange) | EventCallback<DateTime> | 无 |
| onChange | 日期变化回调 | EventCallback<DateTime> | 无 |
| headerRender | 自定义头部内容 | Func<CalendarHeaderRenderArgs, RenderFragment> | 无 |
value 与 defaultValue
源码中 Value 参数 默认值为DateTime.Now,表示当前展示的日期;DefaultValue 参数 的 setter 在赋值时会同步覆盖Value,因此二者是联动关系——设置DefaultValue等价于同时设置了初始展示日期:
public DateTime DefaultValue { get => _defaultValue; set { _defaultValue = value; Value = _defaultValue; // 同步写入 Value } }fullscreen 全屏与卡片模式
FullScreen默认true,即日历占据父容器全部可用宽度。当它设置为false时,日历会以紧凑卡片形态呈现,适合嵌套在空间有限的容器中(如侧边栏面板)。对应 CSS 类映射在 SetClass 方法 中:FullScreen = true时追加ant-picker-calendar-full类,false时不追加。
官方示例 Card_.razor 展示了卡片模式,将日历放入一个 300px 宽的带边框容器中:
<div class="site-calendar-demo-card"> <Calendar FullScreen="@false" OnPanelChange="OnPanelChange" /> </div>disabledDate 禁用日期
disabledDate接收一个Func<DateTime, bool>委托,返回true的日期将不可选择。该参数在 Calendar.razor.cs 中声明,默认值为null(即全部日期可选),可直接在 Razor 中传入 Lambda:
<Calendar DisabledDate="date => date.DayOfWeek == DayOfWeek.Sunday" />validRange 可显示范围
validRange为DateTime[](两个元素的数组,分别表示起止日期),用于限定日历中可显示的日期区间。值得注意的源码细节:在 OnInitialized 方法 中,初始化时会检查当前Value是否越界——若Value小于范围下界则钳制到下界,大于上界则钳制到上界,确保初始展示日期始终落在合法范围内:
if (ValidRange != null) { if (Value < ValidRange[0]) { Value = ValidRange[0]; } else if (Value > ValidRange[1]) { Value = ValidRange[1]; } }mode 初始模式
mode决定日历初始展示的是月份面板(CalendarMode.Month,即逐日视图)还是年份面板(CalendarMode.Year,即逐月视图),默认CalendarMode.Month。枚举定义见 CalendarMode.cs。源码中 Mode 会被映射为日期选择器的面板粒度:Month → DatePickerType.Date、Year → DatePickerType.Month(见 OnInitialized),并由内部组件 CalendarPanelChooser 负责实际渲染对应粒度的面板。
locale 国际化与 moment locale 前置条件
官方文档特别提醒:Calendar 部分 locale 是从 value(日期值)中读取的,因此请先正确设置 moment 的 locale。默认语言为en-US,若需使用其他语言,推荐在应用入口文件全局设置 locale,例如:
// import moment from 'moment'; // import 'moment/locale/zh-cn'; // moment.locale('zh-cn');在源码层面,Locale 参数 的类型为DatePickerLocale,其默认值取自全局LocaleProvider.CurrentLocale.DatePicker;同时组件还暴露了 CultureInfo 参数(默认取LocaleProvider.CurrentLocale.CurrentCulture),用于日期格式化——例如 GetFormatValue 方法 即以该 CultureInfo 格式化日期字符串。因此,要获得正确的中文月名、周起始日等展示效果,需要在初始化时同步配置 moment locale 与项目的 LocaleProvider。仓库各语言资源位于 components/locales(如 zh-CN.json),可供参考。
事件回调机制:onSelect、onChange 与 onPanelChange
组件内部点击日期时统一走 OnSelectValue 方法,其调用链清晰揭示了三个事件的关系:
protected void OnSelectValue(DateTime date) { Value = date; // 1. 更新当前值 OnSelect.InvokeAsync(date); // 2. 触发 onSelect OnChange.InvokeAsync(date); // 3. 触发 onChange StateHasChanged(); }- onSelect:点击选择日期时触发。源码中该参数已标注
[Obsolete("Use OnChange instead")](Calendar.razor.cs),官方建议新代码改用onChange; - onChange:日期变化时触发(点击选择同样会触发),是当前推荐的选择回调;
- onPanelChange:仅当用户在年月面板之间切换时触发(见下方 ChangeMode)。
ChangeMode:面板切换的底层实现
当用户点击头部切换视图时,ChangeMode 方法 会完成模式更新、记录前一个面板类型(_prePickerStack用于回退)并触发OnPanelChange回调:
internal void ChangeMode(CalendarMode mode) { Mode = mode; DatePickerType picker = Mode switch { CalendarMode.Month => DatePickerType.Date, CalendarMode.Year => DatePickerType.Month, _ => DatePickerType.Date }; _prePickerStack.Push(_picker); _picker = picker; OnPanelChange?.Invoke(PickerValues[0], _picker); StateHasChanged(); }自定义日期/月份单元格:dateCellRender 与 monthCellRender
日历组件最核心的扩展点就是四个单元格渲染函数。理解"追加"与"覆盖"的区别至关重要:
dateCellRender/monthCellRender:返回的RenderFragment会被追加到单元格默认内容之后;dateFullCellRender/monthFullCellRender:返回内容会整体覆盖单元格(包括默认的日期数字)。
两者均接收DateTime参数,代表当前要渲染的日期/月份。
官方示例 NoticeCalendar.razor 是经典的事件日历实现:按日期维护一份事件列表,用dateCellRender在日期格内追加 Badge 事件条目,用monthCellRender在月份格内追加"Backlog number"统计数字:
<Calendar DateCellRender="DateCellRender" MonthCellRender="MonthCellRender" /> @code { private RenderFragment DateCellRender(DateTime value) { var listData = GetListData(value); // 按 value.Day 返回当天事件列表 return @<Template> <ul class="events"> @foreach (var data in listData) { <li key="@data.content"> <Badge Status="@data.type" Text="@data.content" /> </li> } </ul> </Template>; } private RenderFragment MonthCellRender(DateTime value) { int? num = GetMonthData(value); // 如 8 月返回 1394 if (num == null) return null; return @<Template> <div className="notes-month"> <section>@num</section> <span>Backlog number</span> </div> </Template>; } }该示例同时展示了组件与 Badge 的组合用法:将BadgeStatus(Warning/Success/Error)映射为徽标状态,在日期格内呈现事件语义。参考样式.events与.notes-month位于同一示例文件末尾的<Style>块中,用于控制事件列表的溢出省略与月份统计的居中排版。
自定义头部:headerRender
headerRender允许完全替换日历顶部的年月切换区域,其参数类型为CalendarHeaderRenderArgs(定义见 CalendarHeaderRenderCallback.cs),包含四个成员:
| 成员 | 类型 | 说明 |
|---|---|---|
| Value | DateTime | 当前面板代表日期 |
| Type | CalendarMode | 当前模式(Month / Year) |
| OnChange | Action<DateTime> | 变更日期的回调(切换年/月后调用) |
| OnTypeChange | Action<CalendarMode> | 变更模式回调(切换 Month/Year 面板) |
组件模板逻辑见 Calendar.razor:当HeaderRender不为 null 时直接调用自定义头部;否则渲染内置的CalendarHeader组件。
官方示例 CustomizeHeader.razor 实现了一个"标题 + RadioGroup 模式切换 + 年份下拉 + 月份下拉"的完全自定义头部,其中调用args.OnTypeChange与args.OnChange驱动组件内部状态的关键代码如下:
<Calendar FullScreen="@false" HeaderRender="HeaderRender" OnPanelChange="OnPanelChange" /> @code { private RenderFragment HeaderRender(CalendarHeaderRenderArgs args) { int month = args.Value.Month; int year = args.Value.Year; return @<Template> <div style="padding: 8px"> <Title Level="4">Custom header</Title> <Row Gutter="8"> <AntDesign.Col> <RadioGroup Size="InputSize.Small" OnChange="value => args.OnTypeChange(value)" Value="@args.Type" TValue="CalendarMode"> <Radio RadioButton Value="CalendarMode.Month">Month</Radio> <Radio RadioButton Value="CalendarMode.Year">Year</Radio> </RadioGroup> </AntDesign.Col> <AntDesign.Col> <select @onchange="e => OnSelectYear(e, args)" value="@year"> @GetYearOptions(year) </select> </AntDesign.Col> <AntDesign.Col> <select @onchange="e => OnSelectMonth(e, args)" value="@month"> @GetMonthOptions() </select> </AntDesign.Col> </Row> </div> </Template>; } private void OnSelectYear(ChangeEventArgs args, CalendarHeaderRenderArgs renderArgs) { int year = Convert.ToInt32(args.Value); renderArgs.OnChange(DateHelper.CombineNewDate(renderArgs.Value, year: year)); } private void OnSelectMonth(ChangeEventArgs args, CalendarHeaderRenderArgs renderArgs) { int month = Convert.ToInt32(args.Value); renderArgs.OnChange(DateHelper.CombineNewDate(renderArgs.Value, month: month)); } }注意示例中通过DateHelper.CombineNewDate(工具类定义于 components/core/Helpers)在保留原日期其他字段的基础上合成新日期,再传给OnChange,从而驱动面板日期更新。头部内的OnTypeChange会最终路由到源码中的ChangeMode,触发OnPanelChange回调。
源码级原理小结
将文档 API 与源码 Calendar.razor.cs 对照,可以归纳出组件设计的几个关键机制:
- 面板粒度映射:
CalendarMode.Month/Year与DatePickerType.Date/Month一一对应,组件复用日期选择器的面板渲染体系(内部使用CalendarPanelChooser,见 Calendar.razor); - 值联动:
DefaultValue的 setter 同步写入Value,ValidRange在初始化时对Value做越界钳制; - 回调分层:点击日期统一经过
OnSelectValue,同时触发OnSelect(已废弃)与OnChange;面板切换单独走ChangeMode并触发OnPanelChange,且用_prePickerStack栈记录面板切换历史以支持回退; - 渲染扩展点:头部可由
HeaderRender完全接管(CalendarHeaderRenderArgs提供值、模式与两个驱动回调),单元格则由追加式(dateCellRender/monthCellRender)与覆盖式(dateFullCellRender/monthFullCellRender)两类委托提供两级自定义能力; - 国际化依赖:locale 从日期值与全局
LocaleProvider.CurrentLocale读取,使用前需正确配置 moment locale(默认 en-US)。
实战建议
- 日程/事件日历:优先使用
dateCellRender+ Badge 组合,事件数据按日期分组后在单元格内追加展示; - 价格日历/课表:若需要完全掌控单元格内容(隐藏默认日期数字),使用
dateFullCellRender/monthFullCellRender覆盖式渲染; - 嵌入窄容器:设置
FullScreen="@false"并配合自定义容器宽度(参考 Card_.razor 的 300px 卡片写法); - 品牌化头部:通过
headerRender重写年月切换器,可在其中混合使用 RadioGroup、Select 等组件与原生<select>; - 注意 API 演进:
onSelect已标记[Obsolete],新代码应统一使用onChange响应日期选择。
- UI组件
- 前端
【免费下载链接】ant-design-blazor
🌈A rich set of enterprise-class UI components based on Ant Design and Blazor.
相关推荐
Ant Design Blazor Calendar 日历组件完整指南:API 参数、单元格自定义与面板切换原理
Ant Design Blazor Calendar 日历组件完整指南:API 参数、单元格自定义与面板切换原理 导读 本文围绕 Ant Design Blaz
前端UI组件设计系统Ant Design Blazor Calendar 日历组件完整实战指南:API、自定义渲染与本地化
Ant Design Blazor Calendar 日历组件完整实战指南:API、自定义渲染与本地化 本文围绕 Ant Design Blazor 官方文档
UI组件前端Ant Design Calendar 日历组件深度指南:完整 API、源码实现与自定义渲染实战
Ant Design Calendar 日历组件深度指南:完整 API、源码实现与自定义渲染实战 本文围绕 Ant Design 的 Calendar 日历组件
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考