项目中经常要碰到日期输入,.NET 2.0提供了TDateTimePicker存在如下方面的不足:
- 数据源为空时,绑定的日期不匹配。
- 不能设定日期文本框的颜色。
网上找到的却不能满足数据源绑定的基本要求。
1、基本思路
构思定制日期编选控件时,笔者尝试过如下两个思路:
- 定制MonthCanlendar + 定制TextBox + 定制Button。实际使用时发现,定制MonthCalendar在年份往前选择时存在严重显示上的Bug,并且,在点击非控件区域时不能自动CloseUp。
- 定制DateTimePicker + 定制TextBox。该思路最后证明是成功的。
2、定制文本框控件TDateEditBox
由于笔者以前编写过TNumEditBox,对TextBox有一定的了解,如实直接从TextBox上定制了一个输入日期的控件TDateEditBox。实现的关键技术如下:
- 既然是输入日期,就只允许键盘,必须屏蔽掉上下文菜单。在构造函数中 this.ContextMenu = new ContextMenu()。
- 捕获OnKeyDown事件,处理方向键、数字键、BackSpace、Delete。
- 值需要区分Null、Valid情况。如果有值修改,则需要通知容器控件,满足绑定数据源更新的需求。
3、定制日期选择控件TDatePicker
从DateTimePicker继承,关键技术如下:
- 如何捕获四个输入操作:鼠标点击确认日期、键盘Enter确认日期、ESC放弃选择、鼠标点控件外时自动Close。
- 跟踪WndProc消息发现,鼠标点控件外时发生消息 WM_CAPTURCHANGED,此时表示放弃选择。
- 跟踪WndProc消息发现,Enter、ESC和鼠标点选日期,均产生WM_IME_SETCONTENT消息。于是在ProProcessMessage事件中捕获ESC、Enter消息,在WndProc捕获鼠标选择日期消息。
- ESC消息与鼠标点选日期相似,既有SetContent,从而确认,后有键盘消息,然后放弃。从而使得控件产生一次确认选择、然后放弃的消息。
4、定制UserControl
由前面两个定制控件组成,基本思路如下:
- TDatePicker放在TDateEditBox下,但露出其Button部分,是的下拉框左边对齐。
- 实现INotifyPropertyChanged,满足数据绑定源绑定与刷新要求。实际测试表明,该控件如果设置绑定更新方式为默认,则编辑框与直接给控件赋值时的刷新行为不一致。于是,固定为两种方式:Never、OnPropertyChanged。
有关代码的实现细节,请参考如下全部代码。
4、结语
TDateEditPicker控件还有一些属性,如:设置日期分隔符、设置前景颜色、设置无效日期颜色、最大最小日期等。具体使用时,拷贝上述控件代码为一个类文件,加到自己的Project中然后编译,此时在工具条上将看到该控件。拖拉到窗体上即可。
由于项目中需要用到不少日期处理,原先做过的一个NullableDateTimePicker不能满足要求。在参考网上的一些资料基础上,花了约5天计50多小时。时间仓促,测试不充分,有同行使用时如果发现Bug,请不吝告之,笔者将进一步完善。
升级历史
- 2010-03-13:TDateEditPicker控件及演示(Ver1.3)。修改了部分代码,规范了字段和属性命名,整理了多余代码。
- 2010-03-14:TDateEditPicker演示及源码(Ver1.5)。增加了日期显示格式和分隔符号,增加了设计期初始日期属性,优化了代码。
- 2010-03-16:TDateEditPicker控件及演示(Ver1.6)。解决了通过Tab移动到该控件时输入框不获得聚焦的Bug。
- 2010-03-18:TDateEditPicker控件及演示(Ver1.8)。解决了直接给日期后下拉不显示该日期的一个Bug, 下拉关闭后聚焦的Bug。
- 2010-04-08:Version1.9。在 TabControl 容器中的几个TabPage上,只有一个Page显示DatePicker控件,其他的Page只有DateEditBox控件。
- 2010-04-17:Version1.11。去掉了DataPickerVisible属性(该属性导致控件在TabControl的多页面上异常),增加了ValueChanged事件。