背景
业务开发过程中遇到一个日期范围选择的需求,和element ui的datetimepicker组件比较类似,由两个日历控件组成,联动选择起始时间和结束时间。
问题
wpf中提供了一个datepicker
的控件,主要由datepickertextbox
、button
和一个calendar
组成,其中calendar
是后台代码动态添加的,因此不能直接通过自定义datepicker
的控件模板实现需求。这里通过实现自定义datetimepicker
控件来满足需求。
技术要点与实现
由于calendar
结构比较复杂,本文通过控件组合的方式简单实现自定义datetimepicker
。先来看下实现效果。
首先创建一个名为datetimepicker
的usercontrol,添加依赖属性hoverstart
和hoverend
用于控制日历中的开始日期和结束日期,添加依赖属性datetimerangestart
和datetimerangeend
用于设置外部设置/获取起始时间和结束时间。
然后在xaml中添加两个watermarktextbox
用于输入起始时间和结束时间(增加校验规则验证时间的合法性,这里不再详细说明如何写校验规则,具体可参考)。接着添加一个popup
(默认关闭),并在其中添加两个calendar
用于筛选日期,以及四个combobox
用于筛选小时和分钟。当watermarktextbox
捕获到鼠标时触发popup
打开。
紧接着就是修改calendar
的样式了。通常情况下,自定义控件模板只需要在visual studio的设计窗口或者blend中选中控件,然后右键菜单中编辑模板即可。可能由于calendar中的部分元素(calendarbutton
和calendardaybutton
)是后台代码生成,这个方法编辑calendar
模板副本生成的calendarstyle
不包含完整的可视化树结构,无法对样式进一步修改。幸运的是微软官方文档公开了控件的默认样式和模板,在此基础上进行修改即可。通过官方文档可以发现calendar
完整的可视化树中包含了四个类型控件calendar
、calendaritem
、calendarbutton
、calendardaybutton
。其中calendardaybutton
对应的就是日历中具体的“天”,管理着具体的“天”的状态,比如选中状态、不可选状态等,这也是我们主要修改的地方,接下来看下calendardaybutton
的样式。(其他几个元素的样式和模板参照官方文档修改即可)
样式中用到一个multibinding
绑定calendardaybutton
以及前边提到的两个依赖属性:hoverstart
和hoverend
,然后通过multivalueconverter
转换器比较calendardaybutton
是否处于选中的日期范围,根据不同的状态设置其背景样式和字体颜色。selecteddatesconverter
的实现如下:
public class selecteddatesconverter : imultivalueconverter
{
public object convert(object[] values, type targettype, object parameter, cultureinfo culture)
{
if (targettype.name == "cornerradius")
{
if (values.length < 3) return new cornerradius(0);
if (values[0].equals(values[1])) return new cornerradius(13, 0, 0, 13);
else if (values[0].equals(values[2])) return new cornerradius(0, 13, 13, 0);
else return new cornerradius(0);
}
else
{
if (values.length < 3) return visibility.collapsed;
if ((values[0].equals(values[1]) || values[0].equals(values[2])) && system.convert.toboolean(values[3]) == false) return visibility.visible;
else return visibility.collapsed;
}
}
public object[] convertback(object value, type[] targettypes, object parameter, cultureinfo culture)
{
throw new notimplementedexception();
}
}
最后就是在后台代码中根据日历的selecteddateschanged
事件设置hoverstart
和hoverend
的值,以此来控制datetimepicker
中选中日期的样式。
总结
本文分享了一种简单实现自定义datetimepicker
控件的方式,同时也介绍了另外一种查看原生控件默认样式和模板的方法:查看。这种方法虽然不如在visual studio的设计窗口或者blend中编辑模板副本方便,但提供了完整的结构、每个元素的组成部分以及可视化状态,方便开发人员清晰的了解控件全貌,可以应对修改复杂的原生控件样式和模板的需求。