[UWP]了解模板化控件(2):模仿ContentControl
ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高。ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTree上的UI元素的父节点中总有一个ContentControl或Panel。
因为ContentControl很简单,如果只实现ContentControl最基本功能的话很适合用来做TemplatedControl的入门。这次的内容就是模仿ContentControl实现一个模板化控件MyContentControl,直接继承自Control。
1. 定义属性
/// <summary>
/// 获取或设置Content的值
/// </summary>
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
/// <summary>
/// 标识 Content 依赖属性。
/// </summary>
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(MyContentControl), new PropertyMetadata(null, OnContentChanged));
private static void OnContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
MyContentControl target = obj as MyContentControl;
object oldValue = (object)args.OldValue;
object newValue = (object)args.NewValue;
if (oldValue != newValue)
target.OnContentChanged(oldValue, newValue);
}
protected virtual void OnContentChanged(object oldValue, object newValue)
{
}
/// <summary>
/// 获取或设置ContentTemplate的值
/// </summary>
public DataTemplate ContentTemplate
{
get { return (DataTemplate)GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); }
}
/// <summary>
/// 标识 ContentTemplate 依赖属性。
/// </summary>
public static readonly DependencyProperty ContentTemplateProperty =
DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(MyContentControl), new PropertyMetadata(null, OnContentTemplateChanged));
private static void OnContentTemplateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
MyContentControl target = obj as MyContentControl;
DataTemplate oldValue = (DataTemplate)args.OldValue;
DataTemplate newValue = (DataTemplate)args.NewValue;
if (oldValue != newValue)
target.OnContentTemplateChanged(oldValue, newValue);
}
protected virtual void OnContentTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
{
}
MyContentControl只实现ContentControl两个最常用的属性:Content和ContentTemplate。两个都需要使用依赖属性,这样才可以使用Binding和下面会用到的TemplateBinding。
通常重要的属性都会定义一个通知属性值变更的virtual方法给派生类使用,如这里的protected virtual void OnContentChanged(object oldValue, object newValue)。为了可以定义virtual方法,要移除类的sealed关键字。
值得一提的是Content属性的类型是Object,这样Content中既可以放文字,也可以放图片、Panel等元素。在UWP中如无特殊需求,Content、Header、Title等内容属性最好都是Object类型,这样更方便扩展,例如可以在Header放一个Checkbox,这是很常见的做法。
2. 实现外观
2.1 DefaultStyle
<Style TargetType="local:MyContentControl">
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="VerticalContentAlignment"
Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyContentControl">
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
将Themes/Generic.xaml中TargetType="local:MyContentControl"的Style改写成上述XAML。
UWP通过ControlTemplate定义控件的外观。在MyContentControl中,ControlTemplate只有一个元素ContentPresenter,它使用TemplateBinding绑定到自己所在的MyContentControl的公共属性。对经常使用ControlTemplate的开发者来说ContentPresenter和TemplateBinding都不是陌生的概念。
2.2 ContentPresenter
ContentPresenter用于显示内容,默认绑定到ContentControl的Content属性。基本上所有ContentControl中都包含一个ContentPresenter。ContentPresenter直接从FrameworkElement派生。
2.3 TemplateBinding
用于单向绑定ControlTemplate所在控件的功能属性,例如Margin="{TemplateBinding Padding}"几乎等效于Margin="{Binding Margin,RelativeSource={RelativeSource Mode=TemplatedParent},Mode=OneWay}",相当于一种简化的写法。但它们之间有如下不同:
- TemplateBinding只能用在ControlTemplate中。
- TemplateBinding的源和目标属性都必须是依赖属性。
- TemplateBinding不能使用TypeConverter,所以源属性和目标属性必须为相同的数据类型。
通常在ContentPresenter上使用TemplateBinding的属性不会太多,因为很大一部分Control的属性都是可属性值继承的,即默认使用VisualTree上父节点所设置的属性值,譬如字体属性(如FontSize、FontFamily)、DataContext等。
除了可属性值继承的属性,需要适当地将ControlTemplate中的元素属性绑定到所属控件的属性,例如Margin="{TemplateBinding Padding}",这样可以方便控件的使用者通过属性调整UI。
2.4 通过Setter改变默认值
通常从父类继承而来的属性不会在构造函数中设置默认值,而是在DefaultStyle的Setter中设置默认值。MyContentControl为了将HorizontalContentAlignment改为Left而在Style添加了Property="HorizontalContentAlignment"的Setter。
2.5 ContentPropertyAttribute
<local:MyContentControl>
<local:MyContentControl.Content>
<Rectangle Height="100"
Width="100"
Fill="Red" />
</local:MyContentControl.Content>
</local:MyContentControl>
使用MyContentControl的XAML如上所示,但看起来和ContentControl不同,多了 local:MyContentControl.Content 这行。解决办法是添加Windows.UI.Xaml.Markup.ContentPropertyAttribute到MyContentControl上。
[ContentProperty(Name = "Content")]
public class MyContentControl : Control
在MyContentControl使用这个Attribute,UWP在解释XAML时,会将XAML的内容识别为MyContentControl的Content属性。除了可以省略两行XAML外,ContentPropertyAttribute还有指出类的主要属性的作用。譬如Panel添加了[ContentProperty(Name = "Children")],TextBlock添加了[ContentProperty(Name = "Inlines")]。
添加ContentPropertyAttribute后,使用MyContentControl的XAML和ContentControl就基本一致了。
<local:MyContentControl>
<Rectangle Height="100"
Width="100"
Fill="Red" />
</local:MyContentControl>
[UWP]了解模板化控件(2):模仿ContentControl的更多相关文章
- [UWP 自定义控件]了解模板化控件(2):模仿ContentControl
ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高.ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTr ...
- [UWP]了解模板化控件(2.1):理解ContentControl
UWP的UI主要由布局容器和内容控件(ContentControl)组成.布局容器是指Grid.StackPanel等继承自Panel,可以拥有多个子元素的类.与此相对,ContentControl则 ...
- [UWP]了解模板化控件(8):ItemsControl
1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...
- [UWP]了解模板化控件(3):实现HeaderedContentControl
1. 概述 来看看这段XMAL: <StackPanel Width="300"> <TextBox Header="TextBox" /&g ...
- [UWP]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...
- UWP 动画系列之模仿网易云音乐动画
一.前言 最近在弄毕业设计(那时坑爹选了制作个UWP商店的APP),一个人弄得烦躁,在这里记录一些在做毕业设计时的学习过程.由于我的毕业设计是做一个音乐播放器,那么Windows商店上优秀的软件当然是 ...
- [UWP]了解模板化控件(1):基础知识
1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...
- [UWP]了解模板化控件(4):TemplatePart
1. TemplatePart TemplatePart(部件)是指ControlTemplate中的命名元素.控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected ...
- [UWP]了解模板化控件(9):UI指南
1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...
随机推荐
- C++编程练习(2)----“实现简单的线性表的链式存储结构“
单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素. 对于查找操作,单链表的时间复杂度为O(n). 对于插入和删除操作,单链表在确定位置后,插入和删除时间仅为O(1). 单链表不需要分配存储 ...
- Disruptor深入解读
将系统性能优化到极致,永远是程序爱好者所努力的一个方向.在java并发领域,也有很多的实践与创新,小到乐观锁.CAS,大到netty线程模型.纤程Quasar.kilim等.Disruptor是一个轻 ...
- ArcGIS Pro 简明教程(4)工具和模型构建器
ArcGIS Pro 简明教程(4)工具和模型构建器 by 李远祥 工具箱中的工具 ArcGIS Pro 在1.3版本基本上已经继承了ArcMap的所有工具,而且会不断加入一些它自身才有的工具,例如适 ...
- 修改UITextField的占位文字颜色的三种层次
层次一:利用富文本 // 描述占位文字属性 NSMutableDictionary *dict = [NSMutableDictionary dictionary] ; dict[NSForegrou ...
- Sqlserver 链接服务器和同义词
在数据库的日常维护中,经常会遇到跨服务器的数据传输. 例如A服务器上的数据每天要从B服务器上去获取数据,然后插入到自己的服务器上.这种情况就要用到链接服务器了. 接下来,我就把我本机当作服务器A,17 ...
- ios跳转到系统设置
在项目中,我们经常会碰到使用位置的需求.当用户设置app不允许使用位置的时候,最好的用户体验就是直接调转到系统的位置设置界面,进行设置. 第一 跳转到自己项目(在需要调转的按钮动作中添加如下的代码,就 ...
- BZOJ 3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者(贪心)
这= =,就是线段覆盖对了= =直接贪心就行了= = CODE: #include<cstdio>#include<iostream>#include<cstring&g ...
- 简述.jpg .Gif .png-8 .png-24的区别
最近有很多朋友在开发过程中有时候会遇到图片加载不清晰,透明度失真,或者对图片进行操作之后造成图片损耗的现象,在这里给大家简单介绍一下常用的几种图片格式之间的区别 Gif格式特点: 1.透明性,Gif是 ...
- 从HTML5规范弄清i、em、b、strong元素的区别
为了语义化,HTML5增加了不少新标签.其中i.em和b.strong这两组标签是最容易弄混的,不好好去探究一下,还真说不清.这个也是前端面试中经常会问的问题.今天从源头上,也就是从HTML5的文档( ...
- 第33篇 js 常用简单的写法
1.取整 取整可以使用'~~'相当于Math.floor() ~~1.5=1; 2.判断为空或者undefine时赋一个自定义的值 var obj={a:"111",b:" ...