在前两篇文章中,我们分别使用了templatepart和visualstate的方法实现了相同的功能,其中visualstate显然更具灵活性。在这种情况下,我通常更倾向于使用visualstate。然而,在实际应用中,这两种实现方式并不是互斥的,许多模板化控件会同时使用这两种方法。
使用VisualState的好处包括:
- 代码和UI分离,使得控件扩展更灵活。
- 可以使用Blend轻松实现动画。
尽管VisualState有诸多优势,但并不是说所有功能都必须使用VisualState实现。以下情况我会选择使用TemplatePart:
- 需要快速实现一个控件。
- 某个行为是固定的,不需要扩展。
- 需要在代码中操作UI,例如Slider或ComboBox。
- 为了强调某个部件是控件必需的。
- 为了隐藏实现细节,限制派生类或ControlTemplate修改重要的逻辑。
其中,使用TemplatePart产生的扩展性问题是我谨慎使用这种方案的最大因素。
除了VisualState,TemplatePart的功能也常常会被TemplateBinding所替代。前面的例子展示了使用VisualState在UI上的优势,这次我们用另一个控件DateTimeSelector来讨论使用TemplatePart在扩展性上的其他问题。
使用TemplatePart
DateTimeSelector组合了CalendarDatePicker和TimePicker,用于选择日期和时间(SelectedDateTime)。其XAML如下:
相应的代码如下:
可以看出,DateTimeSelector通过监视CalendarDatePicker的DateChanged和TimePicker的TimeChanged来改变SelectedDateTime的值。
DateTimeSelector的代码非常简单,控件也运行良好,但如果某天需要将CalendarDatePicker替换为DatePicker或某个第三方的日期选择控件,DateTimeSelector就无能为力了,既不能通过修改ControlTemplate,也不能通过继承来达到目的。
使用TemplateBinding
通常在构建这类控件时,应先考虑其数据和行为,而不关心其UI。DateTimeSelector最核心的功能是通过选择Date和Time得出组合起来的DateTime,那么就可以先写出如下的类:
控件的代码并不清楚ControlTemplate中包含什么控件,它只关心自己的数据。
在XAML中通过绑定使用这些数据。
这里给出了两个Style,分别使用了CalendarDatePicker和DatePicker,通过TwoWay Binding访问DateTimeSelector2中的Date属性。如果你的TemplatedControl需要有良好的扩展能力,可以尝试使用这种方式。