[UWP 自定义控件]了解模板化控件(3):实现HeaderedContentControl
1. 概述
来看看这段XMAL:
<StackPanel Width="300">
<TextBox Header="TextBox" />
<ComboBox Header="ComboBox" HorizontalAlignment="Stretch"/>
<AutoSuggestBox Header="AutoSuggestBox" />
<TextBlock Text="ListBox" />
<ListBox>
<ListBoxItem Content="ListBoxItem 1" />
<ListBoxItem Content="ListBoxItem 2" />
<ListBoxItem Content="ListBoxItem 3" />
</ListBox>
</StackPanel>

是不是觉得它们中出了一个叛徒?这个示例中除了ListBox控件其它都自带Header,但是ListBox没有Header属性,只好用一个TextBlock模仿它的Header。这样就带来一个问题:只有ListBox的Header高度和其它控件不一致。
既然现在讨论的是自定义控件,这里就用自定义控件的方式解决这个问题。首先想到最简单的方法,就是自定义一个HeaderedContentControl,如名字所示,这个控件继承自ContentControl并拥有Header属性,用起来大概是这样:
<HeaderedContentControl Header="ListBox">
<ListBox/>
</HeaderedContentControl>
这样,只要在HeaderedContentControl的样式中模仿其它含Header属性的控件,就能统一Header的外观。
WPF中本来就有这个控件,它是Expander、GroupBox、TabItem等诸多拥有Header属性的控件的基类,十分方便好用。UWP中模仿这个控件很简单,而且很适合用来学习自定义控件的进阶知识。
2. 定义HeaderedContentControl结构
比起WPF,借鉴Silverlight的HeaderedContentControl比较好,因为Silverlight的比较简单。HeaderedContentControl只需要在继承ContentControl后添加两个属性:Header和HeaderTemplate。
public class HeaderedContentControl : ContentControl
{
public HeaderedContentControl()
{
this.DefaultStyleKey = typeof(HeaderedContentControl);
}
/// <summary>
/// 获取或设置Header的值
/// </summary>
public object Header
{
get { return (object)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
/// <summary>
/// 标识 Header 依赖属性。
/// </summary>
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(object), typeof(HeaderedContentControl), new PropertyMetadata(null, OnHeaderChanged));
private static void OnHeaderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
HeaderedContentControl target = obj as HeaderedContentControl;
object oldValue = (object)args.OldValue;
object newValue = (object)args.NewValue;
if (oldValue != newValue)
target.OnHeaderChanged(oldValue, newValue);
}
/// <summary>
/// 获取或设置HeaderTemplate的值
/// </summary>
public DataTemplate HeaderTemplate
{
get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
set { SetValue(HeaderTemplateProperty, value); }
}
/// <summary>
/// 标识 HeaderTemplate 依赖属性。
/// </summary>
public static readonly DependencyProperty HeaderTemplateProperty =
DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(HeaderedContentControl), new PropertyMetadata(null, OnHeaderTemplateChanged));
private static void OnHeaderTemplateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
HeaderedContentControl target = obj as HeaderedContentControl;
DataTemplate oldValue = (DataTemplate)args.OldValue;
DataTemplate newValue = (DataTemplate)args.NewValue;
if (oldValue != newValue)
target.OnHeaderTemplateChanged(oldValue, newValue);
}
protected virtual void OnHeaderChanged(object oldValue, object newValue)
{
}
protected virtual void OnHeaderTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
{
}
}
3. 设计样式
在C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.14393.0\Generic\generic.xaml中找到ContentControl的样式。

再从TextBox的Style中找到HeaderContentPresenter

提示: 随便找个有ThemeResource的XAML,譬如
Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}",在资源名称(ApplicationPageBackgroundThemeBrush)上按"F12",即可导航到存放ThemeResource的generic.xaml。
组合起来,HeaderedContentControl的默认样式就完成了。
<Style TargetType="local:HeaderedContentControl">
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="VerticalContentAlignment"
Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:HeaderedContentControl">
<StackPanel>
<ContentPresenter x:Name="HeaderContentPresenter"
Foreground="{ThemeResource TextControlHeaderForeground}"
Margin="0,0,0,8"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Normal" />
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
ContentTransitions="{TemplateBinding ContentTransitions}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
4. 使用
<StackPanel Visibility="Collapsed">
<TextBox Header="TextBox" />
<ComboBox Header="ComboBox"
HorizontalAlignment="Stretch" />
<AutoSuggestBox Header="AutoSuggestBox" />
<local:HeaderedContentControl Header="ListBox">
<ListBox>
<ListBoxItem Content="ListBoxItem 1" />
<ListBoxItem Content="ListBoxItem 2" />
<ListBoxItem Content="ListBoxItem 3" />
</ListBox>
</local:HeaderedContentControl>
</StackPanel>

调用代码及效果。这样外观就统一了。
注意: 我移除了 x:DeferLoadStrategy="Lazy"这句,这个知识点比较适合放在有关性能的主题里讨论。
[UWP 自定义控件]了解模板化控件(3):实现HeaderedContentControl的更多相关文章
- UWP 自定义控件:了解模板化控件 系列文章
UWP自定义控件的入门文章 [UWP 自定义控件]了解模板化控件(1):基础知识 [UWP 自定义控件]了解模板化控件(2):模仿ContentControl [UWP 自定义控件]了解模板化控件(2 ...
- [UWP 自定义控件]了解模板化控件(8):ItemsControl
1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...
- [UWP 自定义控件]了解模板化控件(4):TemplatePart
1. TemplatePart TemplatePart(部件)是指ControlTemplate中的命名元素.控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected ...
- [UWP 自定义控件]了解模板化控件(9):UI指南
1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...
- [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 自定义控件]了解模板化控件(5.2):UserControl vs. TemplatedControl
1. UserControl vs. TemplatedControl 在UWP中自定义控件常常会遇到这个问题:使用UserControl还是TemplatedControl来自定义控件. 1.1 使 ...
随机推荐
- BigDecimal遇到的问题,大伙也说说
一:相除精度丢失的问题 BigDecimal的api除法相对加减乘要实现的复杂多了,只介绍常用的我遇到的问题: 问题:两数相除,如果9/3=3整除没问题,但是10/3=0.33333333...... ...
- RabbitMQ延迟消息学习
准备做一个禁言自动解除的功能,立马想到了订单的超时自动解除,刚好最近在看RabbitMQ的实现,于是想用它实现,查询了相关文档发现确实可以实现,动手编写了这篇短文. 准备工作 1.Erlang安装请参 ...
- [20171128]rman Input or output Memory Buffers.txt
[20171128]rman Input or output Memory Buffers.txt --//做一个简单测试rman 的Input or output Memory Buffers. 1 ...
- 如何创建和还原SQL Server 2005数据库?
在还原SQL Server 2005数据库文件之前,建议先把要还原的数据库文件复制粘贴到某个盘的根目录下,这样便于一会儿找到相关的文件,比如C盘. 先打开SQL Server 2005的Microso ...
- django母版页的使用
母版页用于处理html页面相同部分内容,避免在不同的页面中重复出现 1.添加母版页 再manage.py文件相同目录下添加templates文件夹用于保存母版页html文件 2.添加母版页Base.h ...
- Windows结构化异常处理浅析
近期一直被一个问题所困扰,就是写出来的程序老是出现无故崩溃,有的地方自己知道可能有问题,但是有的地方又根本没办法知道有什么问题.更苦逼的事情是,我们的程序是需要7x24服务客户,虽然不需要实时精准零差 ...
- oracle经验记录
1.添加新User时必须要增加的角色权限:connect.dba.resource 2.添加表空间的语句 create tablespace DEMOSPACE datafile 'D:/test.d ...
- Redis的安装和Jedis的使用
Redis的安装和学习资料 Redis的安装可以参考 https://www.cnblogs.com/dddyyy/p/9763098.html Redis的学习可以参考https://www.cnb ...
- python scrapy 登录知乎过程
前面了解了scrapy框架的大概各个组件的作用, 现在要爬取知乎数据,那么第一步就是要登录! 看下知乎的登录页面发现登录主要是两大接口 一: 登录页面地址,获取登录需要的验证码,如下图 打开知乎登录页 ...
- oracle使用with as提高查询效率
经常在开发过程中会用到视图或组合查询的情况,但由于涉及表数据经常达到千万级别的笛卡尔积,而且一段查询时会反复调用,但结果输出往往不需要那么多,可以使用with将过滤或处理后的结果先缓存到临时表(此处原 ...