原文:[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相关推荐

  1. [UWP]了解模板化控件(7):支持Command

    原文:[UWP]了解模板化控件(7):支持Command 以我的经验来说,要让TemplatedControl支持Command的需求不会很多,大部分情况用附加属性解决这个需求会更便利些,譬如UWPC ...

  2. [UWP]了解模板化控件(6):使用附加属性

    原文:[UWP]了解模板化控件(6):使用附加属性 1. 基本需求 之前的ContentView2添加了PointerOver等效果,和TextBox等本来就有Header的控件放在一起反而变得鹤立鸡 ...

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

    原文:[UWP]了解模板化控件(9):UI指南 1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些 ...

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

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

  5. win10 uwp 按下等待按钮

    我们经常需要一个按钮,在按下时,后台执行Task,这时不能再次按下按钮. 我们使用自定义控件,首先新建一个类,我把它命名是ProgressButton 一个进度条按钮,也就是我们按下时发生进度条,完成 ...

  6. [UWP]创建一个ProgressControl

    原文:[UWP]创建一个ProgressControl 1. 前言 博客园终于新增了UWP的分类,我来为这个分类贡献第一篇博客吧. UWP有很多问题,先不说生态的事情,表单.验证.输入.设计等等一堆基 ...

  7. [UWP]理解及扩展Expander

    1. 前言 最近在自定义Expander的样式,顺便看了看它的源码. Expander控件是一个ContentControl,它通过IsExpanded属性或者通过点击Header中的ToggleBu ...

  8. [UWP]做个调皮的BusyIndicator

    1. 前言 最近突然想要个BusyIndicator.做过WPF开发的程序员对BusyIndicator应该不陌生,Extended WPF Toolkit 提供了BusyIndicator的开源实现 ...

  9. UWP Composition API - GroupListView(二)

    还是先上效果图: 看完了上一篇UWP Composition API - GroupListView(一)的童鞋会问,这不是跟上一篇一样的吗??? 骗点击的?? No,No,其实相对上一个有更简单粗暴 ...

最新文章

  1. _findnext()调试中断,发生访问错误,错误定位到ntdll.dll
  2. c++ 程序时间运算 函数;
  3. cobaltstrike安装_CobaltStrike + Metasploit 组合安装
  4. 免费的源代码编辑器Notepad++ 7.6.2 发布
  5. cad常用字体_2300多种CAD字体免费送给你!绘图必备,一键解决你的CAD文字乱码问题...
  6. 在ffmpeg中加入x264模块
  7. 发布Oracle存储过程包c#代码生成工具(CodeRobot)
  8. 乐max2 android 7.0,辣评烩:​乐Max 2即将升级EUI 6.0 基于安卓7.0!
  9. SourceOffsite安装配置
  10. CSS3 3D旋转魔方
  11. Python爬虫理论 | (2) 网络请求与响应
  12. python pip什么意思_“pip install”和“python -m pip install”有什么区别?
  13. 计算机连接小米usb驱动,小米手机USB驱动电脑版
  14. css精华怎么使用,css橄榄精华好用吗?css橄榄精华评测
  15. 【转载】23种设计模式及案例整理分享(建议收藏)【ps:方便自己收藏】
  16. 爱普生Epson ME OFFICE 620F 一体机驱动
  17. HTML5基础实例(三)
  18. hadoop2.7.2下载
  19. cad转dxf格式文件太大_高版本的CAD图纸怎么转换成低版本的DXF格式?
  20. C51单片机-串行口2-蓝牙模块-应用例程

热门文章

  1. dell服务器T100无法进入系统,DELL服务器开机Alert!Cover was previously removed F1
  2. 正则表达式匹配(动规)
  3. c++基础学习(06)--(时间,输入输出,数据结构)
  4. hashmap put过程_阿里十年技术大咖,教你如何分析1.7中HashMap死循环
  5. dataframe 筛选_Spark.DataFrame与Spark.ML简介
  6. 数学建模3 论文排版注意点
  7. 要有自己的核心竞争力,应对时代变迁
  8. hue 查询 hbase 操作相关参考
  9. 解决:XXX类存在 却报错 Unkonwn class:‘XXX‘ 、@Autoeired 注入失败
  10. 想要转人工智能,程序员该如何学习?(学习路线、知识体系)