[WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互 (3)

使用属性控制状态,并创建一个方法帮助状态间的转换。如上面的UpdateVisualStates(bool useTransitions)。当属性值改变或其它有可能影响VisualState的事件发生都可以调用这个方法,由它统一管理控件的VisualState。注意一个控件应该最多只有几种VisualStateGroup,有限的状态才容易管理。

TemplateVisualStateAttribute协定

自定义控件可以使用TemplateVisualStateAttribute协定声明它的VisualState,用于通知控件的使用者有这些VisualState可用。这很好用,尤其是对于复杂的控件来说。上面代码也包含了这个协定:

[TemplateVisualState(Name = StateExpanded, GroupName = GroupExpansion)] [TemplateVisualState(Name = StateCollapsed, GroupName = GroupExpansion)]

TemplateVisualStateAttribute是可选的,而且就算控件声明了这些VisualState,ControlTemplate也可以不包含它们中的任何一个,并且不会引发异常。

7. Trigger、TemplatePart及VisualState之间的选择

正如Expander所示,Trigger、TemplatePart及VisualState都可以实现类似的功能,像这种三种方式都可以实现同一个功能的情况很常见。

在过去版本的Blend中,编辑ControlTemplate可以看到“状态(States)”、“触发器(Triggers)”、“部件(Parts)”三个面板,现在“部件”面板已经消失了,而“触发器”从Silverlight开始就不再支持,以后也应该不会回归(xaml standard在github上有这方面的讨论(Add Triggers, DataTrigger, EventTrigger,___) [and-or] VisualState · Issue #195 · Microsoft-xaml-standard · GitHub[https://github.com/Microsoft/xaml-standard/issues/195])。现在看起来是VisualState的胜利,其实在Silverlight和UWP中TemplatePart仍是个十分常用的技术,而在WPF中Trigger也工作得很出色。

[WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互

[WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互

如果某个功能三种方案都可以实现,我的选择原则是这样:

需要向控件发出命令的,如响应点击事件,就用TemplatePart;

简单的UI,如隐藏/显示某个元素就用Trigger;

如果要有动画,并且代码量和使用Trigger的话,我会选择用VisualState;

几乎所有WPF的原生控件都提供了VisualState支持,例如Button虽然使用ButtonChrome实现外观,但同时也可以使用VisualState定义外观。有时做自定义控件的时候要考虑为常用的VisualState提供支持。

8. 结语

VisualState是个比较复杂的话题,可以通过我的另一篇文章理解ControlTemplate中的VisualTransition更深入地理解它的用法(虽然是UWP的内容,但对WPF也同样适用)。

即使不自定义控件,学会使用ControlTemplate也是一件好事,下面给出一些有用的参考链接。

9. 参考

创建具有可自定义外观的控件 Microsoft Docs
通过创建 ControlTemplate 自定义现有控件的外观 Microsoft Docs
Control Customization Microsoft Docs
ControlTemplate Class (System_Windows_Controls) Microsoft Docs

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjgfw.html