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的更多相关文章

  1. [UWP]了解模板化控件(5.1):TemplatePart vs. VisualState

    1. TemplatePart vs. VisualState 在前面两篇文章中分别使用了TemplatePart及VisualState的方式实现了相同的功能,其中明显VisualState的方式更 ...

  2. [UWP]了解模板化控件(8):ItemsControl

    1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...

  3. [UWP]了解模板化控件(10):原则与技巧

    1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...

  4. [UWP]了解模板化控件(1):基础知识

    1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...

  5. [UWP]了解模板化控件(2):模仿ContentControl

    ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高.ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTr ...

  6. [UWP]了解模板化控件(5):VisualState

    1. 功能需求 使用TemplatePart实现上篇文章的两个需求(Header为空时隐藏HeaderContentPresenter,鼠标没有放在控件上时HeaderContentPresent半透 ...

  7. [UWP]了解模板化控件(9):UI指南

    1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...

  8. [UWP]了解模板化控件(2.1):理解ContentControl

    UWP的UI主要由布局容器和内容控件(ContentControl)组成.布局容器是指Grid.StackPanel等继承自Panel,可以拥有多个子元素的类.与此相对,ContentControl则 ...

  9. [UWP]了解模板化控件(3):实现HeaderedContentControl

    1. 概述 来看看这段XMAL: <StackPanel Width="300"> <TextBox Header="TextBox" /&g ...

随机推荐

  1. SimpleDateFormat的线程安全问题与解决方案

    SimpleDateFormat 是 Java 中一个常用的类,该类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调试的问题. 因为 DateFormat 和 Simple ...

  2. 把时间还给洞察,且看PPT调研报告自动生成攻略

    文/JSong @2017.02.28 在数据分析里面有一句话是说,80%的时间要用于数据清洗和整理,而我觉得理想的状态应该是把更多的把时间花在数据背后的洞察当中.去年11月在简书占了个坑,说要自己写 ...

  3. 深入理解ajax系列第三篇——头部信息

    前面的话 每个HTTP请求和响应都会带有相应的头部信息,其中有的对开发人员有用.XHR对象提供了操作头部信息的方法.本文将详细介绍HTTP的头部信息 默认信息 默认情况下,在发送XHR请求的同时,还会 ...

  4. C#集合的应用以及和数组比较,它的好处有哪些

    我们用的比较多的非泛型集合类主要有 ArrayList类 和 HashTable类.我们经常用HashTable 来存储将要写入到数据库或者返回的信息,在这之间要不断的进行类型的转化,增加了系统装箱和 ...

  5. 1819: [JSOI]Word Query电子字典

    1819: [JSOI]Word Query电子字典 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 729  Solved: 238[Submit][S ...

  6. UI 事件处理

    一 > 事件的基本概念 事件概述 : UIEvent  ( [ɪ’vent]事件  )  事件,是由硬件捕捉的一个表示用户操作设备的对象 分三类 : 触摸事件 ,晃动事件 ,远程控制事件 触摸事 ...

  7. node删除当前文件底下全部文件的正确姿势

    今天在项目上犯了一个很愚蠢的错误 执行如下,结果删除掉了项目根目录底下的所有配置文件,导致本地虚拟机挂掉,这次多一个/的给我教训真是莫大的...哎 正确的姿势为:

  8. Qt下实现简单的UDP通信

    本人呢还是小实习生一枚,刚一脚踏进社会大母亲的怀抱,不想找工作的时候碰到的全是培训机构... 不过还是幸运的进了一家...咳咳,国企?!好吧,其实是国企下面的一个分出来的小公司(正在起步中,算是创业公 ...

  9. i++和++i 的学习

    在<<C++ Primer>> 第四版 5.5中讲解到: ++i:返回操作之后的结果,且是个左值. i++:返回操作之前的结果,且是有右值. // 2_3.cpp : 定义控制 ...

  10. Android知识点网址

    1.proguard字段详解 http://blog.csdn.net/jddkdd2/article/details/8858909 2.android提示框(时间,普通单选.多选对话框),常用控件 ...