[UWP]了解模板化控件(4):TemplatePart
1. TemplatePart
TemplatePart(部件)是指ControlTemplate中的命名元素。控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected DependencyObject GetTemplateChild(String childName)
获取它们后进行操作。
以AutoSuggestBox为例,它的ControlTemplate结构如下,可以看到AutoSuggestBox由四个TemplatePart组成,每个TemplatePart都可以在控件代码中以编程方式访问:
下图显示了AutoSuggestBox的TemplatePart:
2. 使用TemplatePart
上一篇文章构造了一个很基础的控件HeaderedContentControl,这次通过扩展这个类做些试验性质的功能来介绍模板化控件的进阶知识。
新建一个名为ContentView的控件,继承自HeaderedContentControl,它要实现的功能有两个:
- 控件的Header默认Opacity=0.7,当鼠标移动到控件上时,设置Header的Opacity=1。
- 当Header为空时,隐藏用于显示Header的HeaderContentPresenter。
虽然可以使用依赖属性及TemplateBinding的方式实现这个需求,不过这次用TemplatePart的方式实现。很显然,要实现这次的需求最直接的做法是获取显示Header的TemplatePart,然后用代码对其进行操作。大致上分为两步:添加TemplatePart名称,在代码中获取这个部件并操作。
2.1 添加TemplatePart名称
在ContentView的ControlTemplate中为ContentPresenter命名为HeaderContentPresenter:
<ContentPresenter x:Name="HeaderContentPresenter"
Foreground="{ThemeResource TextControlHeaderForeground}"
Margin="0,0,0,8"
FontWeight="Normal"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" />
2.2 获取TemplatePart
模板化控件在加载ControlTemplate后会调用OnApplyTemplate,可以在这个函数中调用protected DependencyObject GetTemplateChild(String childName)
获取模板中指定名字的部件。从返回值是DependencyObject可以看出,只要是DependencyObject 都能使用ControlTemplate获取。
这段代码演示了如何获得显示Header的ContentPresenter部件:
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_headerPart = GetTemplateChild(HeaderPartName) as FrameworkElement;
}
注意:不要在Loaded事件中尝试调用GetTemplateChild,因为Loaded在OnApplyTemplate前调用,而且Loaded更容易被多次触发。
由于Template可能多次加载,或者不能正确获取TemplatePart,所以使用TemplatePart前应该先判断是否为空;如果要订阅事件,应该先取消订阅。更完整的GetTemplateChild步骤应该是:
- 取消订阅TemplatePart事件
- 将TemplatePart存储到私有字段
- 订阅TemplatePart事件
可以参考如下代码:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (_button != null)
{
_button.Click -= OnButtonClick;
}
_button = GetTemplateChild(PartButtonName) as ButtonBase;
if (_button != null)
{
_button.Click += OnButtonClick;
}
}
2.3 完整的代码
[TemplatePart(Name = HeaderPartName, Type = typeof(FrameworkElement))]
public sealed class ContentView : HeaderedContentControl
{
public const string HeaderPartName = "HeaderContentPresenter";
public ContentView()
{
this.DefaultStyleKey = typeof(ContentView);
}
private FrameworkElement _headerPart;
private bool _isPointerEntered;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_headerPart = GetTemplateChild(HeaderPartName) as FrameworkElement;
UpdateHeaderVisual();
}
protected override void OnPointerEntered(PointerRoutedEventArgs e)
{
base.OnPointerEntered(e);
_isPointerEntered = true;
UpdateHeaderVisual();
}
protected override void OnPointerExited(PointerRoutedEventArgs e)
{
base.OnPointerExited(e);
_isPointerEntered = false;
UpdateHeaderVisual();
}
protected override void OnHeaderChanged(object oldValue, object newValue)
{
base.OnHeaderChanged(oldValue, newValue);
UpdateHeaderVisual();
}
private void UpdateHeaderVisual()
{
if (_headerPart == null)
return;
if (_isPointerEntered)
_headerPart.Opacity = 1;
else
_headerPart.Opacity = 0.7;
if (Header == null)
_headerPart.Visibility = Visibility.Collapsed;
else
_headerPart.Visibility = Visibility.Visible;
}
}
3. x:DeferLoadStrategy="Lazy"与GetTemplateChild
标记为x:DeferLoadStrategy="Lazy"
的元素将延迟加载,即不会出现在VisualTree上,直到它被调用。
假设将ContentView中HeaderContentPresenter标记为x:DeferLoadStrategy="Lazy"
并且在代码中注释_headerPart = GetTemplateChild(HeaderPartName) as FrameworkElement
这句,运行时将看不到Header的内容,并且VisualTree如下所示:
只有代码中执行了_headerPart = GetTemplateChild(HeaderPartName) as FrameworkElement
这句后,VisualTree上才可以看到HeaderContentPresenter,如下所示:
出于性能方面的考虑,很多UWP原生控件都会包含x:DeferLoadStrategy="Lazy"
。
4. TemplatePartAttribute协定
有时,为了表明控件期待在ControlTemplate存在某个特定部件,防止编辑ControlTemplate的开发人员删除它,控件上会添加添加TemplatePartAttribute协定。上面的ContentView代码中即包含这个协定:
[TemplatePart(Name = HeaderPartName, Type = typeof(FrameworkElement))]
这段代码的意思是期待在ControlTemplate中存在名称为 "HeaderContentPresenter",类型为FrameworkElement的部件。
TemplatePartAttribute在UWP中的作用好像被弱化了,不止在UWP原生控件中见不到TemplatePartAttribute,甚至在Blend中“部件”窗口也消失了。可能UWP更加建议使用VisualState。
注意:你可能会在别的地方看到部件的命名为“PART_”开头,在WPF时代确实是这样,到现在仍有很多人保留了这种习惯。新兴的命名语法更加自然,不需要加上“PART_”开头。不过既然Blend中没有了“部件”窗口,用“PART_”标识部件也是个不错的方法。
5. 原则
使用TemplatePart需要遵循以下原则:
- 尽可能减少TemplarePartAttribute协定。
- 在使用TemplatePart之前检查其是否为Null。
- 如果ControlTemplate没有遵循TemplatePartAttribute协定也不应该抛出异常,有可能ControlTemplate的作者是故意屏蔽某项功能。
[UWP]了解模板化控件(4):TemplatePart的更多相关文章
- [UWP]了解模板化控件(5.1):TemplatePart vs. VisualState
1. TemplatePart vs. VisualState 在前面两篇文章中分别使用了TemplatePart及VisualState的方式实现了相同的功能,其中明显VisualState的方式更 ...
- [UWP]了解模板化控件(8):ItemsControl
1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...
- [UWP]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...
- [UWP]了解模板化控件(1):基础知识
1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...
- [UWP]了解模板化控件(2):模仿ContentControl
ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高.ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTr ...
- [UWP]了解模板化控件(5):VisualState
1. 功能需求 使用TemplatePart实现上篇文章的两个需求(Header为空时隐藏HeaderContentPresenter,鼠标没有放在控件上时HeaderContentPresent半透 ...
- [UWP]了解模板化控件(9):UI指南
1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...
- [UWP]了解模板化控件(2.1):理解ContentControl
UWP的UI主要由布局容器和内容控件(ContentControl)组成.布局容器是指Grid.StackPanel等继承自Panel,可以拥有多个子元素的类.与此相对,ContentControl则 ...
- [UWP]了解模板化控件(3):实现HeaderedContentControl
1. 概述 来看看这段XMAL: <StackPanel Width="300"> <TextBox Header="TextBox" /&g ...
随机推荐
- 类中的两大类(string类、math类)的应用
类是我们在学习C#的过程中很关键也是特别容易让人蒙逼得地方,类的应用直接可以调用它的属性和方法来进行判断和验证 string类(也叫字符串类) C#中的String类很有用,下面是一些它的常用方法的总 ...
- SpringCache缓存初探
body,table tr { background-color: #fff } table tr td,table tr th { border: 1px solid #ccc; text-alig ...
- 咖啡师之路:第一日一杯Espresso
代码敲累了.产品要发布了.熬夜啊加班啊. 精神完全不在状态. 咋办--- 咋办--- 咋办---! 来一杯Espresso浓缩咖啡.各位码农,码神们的必备良品! 咖啡每天要2-3杯,不管是速溶还是现磨 ...
- 3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者
3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 71 Solve ...
- 《Django By Example》第九章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者@ucag 注:哈哈哈,第九章终于来啦 ...
- ANdrod Studio查看Sha1的方法
在用Studio做开发中,有时候根据业务需求,需要集成一些SDk,举个例子,百度的鹰眼定位,当然还有很多,在创建项目的时候需要输入sha1值,这个sha1值的获取有多种方式,我记得百度有个检测,就可以 ...
- [编织消息框架][设计协议]opCode
OpCode的全称 OpCode(Operation Code) 操作码的意思. OpCode 有几种域组成,不同领域格式组成不同 1.指令号 2.数据范围 3.数据内容 如 {code}{addr ...
- 【js数据结构】栈解决括号不匹配问题
栈可以用来判断一个算术表达式中的括号是否匹配. 思路:读取算术表达式,遇到左括号'{'.'['.'('压入栈,栈的特点是后入先出,所以当遇到右括号'}'.']'.')'的时候,取出栈顶元素,是否满足读 ...
- (转载)一个生动的NIO描述
原文地址:http://blog.csdn.net/zhouhl_cn/article/details/6568119 传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大 ...
- POJ 2182 解题报告
Lost Cows Time Limit: 1000 MS Memory Limit: 65536 KB Description N (2 <= N <= 8,000) cows have ...