WPF——Control与Template理解
文章目录
- 一、前言
- 二、控件
- 三、模板
- 3.1 DataTemplate
- 3.2 ControlTemplate
- 3.3 ContentPresenter
- 四、结语
一、前言
最近又翻看了下刘铁猛的《深入浅出WPF》,发现对模板章节中的部分内容有了更深的体会,所以写篇文扯扯。
文章标题是Control与Template,翻译成控件与模板。
将两者放一起不是无缘无故的,在WPF中,两者关系相当密切。
二、控件
如果有人问你什么是控件,你会怎么回答?
像界面上的按钮、文本框、滚动条啊,都是控件。
所以控件可以理解为界面的组成元素。
以上是我之前的回答,这样的回答没啥问题,很直观,很多人听了都能明白,虽然可能不太严谨。
不过如果继续问,
那为什么叫控件,而不是其它名称。这种界面上的可交互的图案,称作图形元素,不是更直观、更贴切?
我可能会回答,控件这个名称是根据Control这个单词翻译过来的,所以叫控件。
对面可能继续问,那为什么英文叫Control,而不是Graphic Element。
此时我陷入了沉思,确实,为什么会用Control这个单词来命名呢。
如何消除这个疑惑,最简单的就是百度,
百度词条
控件是指对数据和方法的封装。
控件可以有自己的属性和方法,其中属性是控件数据的简单访问者,方法则是控件的一些简单而可见的功能、控件创建过程包含设计、开发、调试工作,然后是控件的使用。
先别管百度说的是对是错,但显然并不直观。你跟非程序员这么解释,他可能还是不懂。
那么从专业的角度来看,这段对控件的描述有没有道理呢。
学过程序的都知道,程序的本质是数据结构与算法。
界面上的控件呈现了一定内容,并且提供了一些交互方式,用户通过交互可以改变程序状态。
那么,控件呈现的内容本质是什么?
是数据。
控件提供的交互方式本质/目的是什么?
我想应该是程序逻辑,比如你按下按钮,最终程序内部会执行一个方法(一段代码,即程序逻辑),程序状态可能会因此发生一些改变。
所以,控件既是数据的表现形式,以让用户可以直观地看到数据;又是算法的表现形式,以让用户方便地操作逻辑。
作为“表现形式”,每个控件都是为了实现某种用户操作算法和直观显示某种数据而生的,一个控件看上去是什么样子由它的“算法内容”和“数据内容”决定,这也是哲学中常说的内容决定形式。
这样看来,百度的这段话还是挺有道理的,更接近控件的本质。
三、模板
接下来回到WPF中的模板。
在以往的GUI技术中,控件内部的逻辑和数据是固定的,程序员不能改变(比如WinForms/Qt(QWidget));对于控件的外观,程序员能做的改变也非常有限,一般就是设置控件的属性,想改变控件的内部结构更是不可能。如果想扩展一个控件的功能或更改其外观让其更适合业务逻辑,哪怕只有一丁点改变,也经常要创建控件的子类或创建用户控件(UserControl)。造成这个局面的根本原因就是数据和算法的“形式”与“内容”耦合的太紧了 。
在WPF中,通过引入模板(Template),将数据和算法的“内容”和“形式”解耦了。
WPF中的Template分为两大类:
- ControlTemplate,是算法内容的表现形式,一个控件怎样组织其内部结构才能让它更符合业务逻辑、让用户操作起来更舒服就是由它来控制的。它决定了控件“长成什么样子”,并让程序员有机会在控件原有的内部逻辑基础上扩展自己的逻辑。
- DataTemplate,是数据内容的表现形式,一条数据显示成什么样子,是简单文本还是直观的图形动画就由它来决定。
一言蔽之,Template就是“外衣”——ControlTemplate是控件的外衣,DataTemplate是数据的外衣。
WPF中的控件不再具有固定的形象,仅仅是算法内容和数据内容的载体。
你可以把控件理解为一组操作逻辑穿上了一套衣服,换套衣服就能变成另外一个模样。你看到的控件默认形象实际上就是出厂时微软为它穿上的默认服装。
3.1 DataTemplate
在实际项目中,DataTemplate相较于ControlTemplate,往往是用的比较多一些。
正如其名,它是数据的模板/外观。使用时,往往会在外层绑上一个对象,然后在内部将对象的属性绑定到各种控件。
只要将绑定的控件更换,显示的外观也会发生变化。即一样的内容可以用不同形式来展现,软件设计称之为“数据-视图”(Data-View)模式。
在WPF开发中,DataTemplate常用的地方有三处,分别是:
- ContentControl的ContentTemplate属性,相当于给ContentControl的内容穿衣服。
- ItemsControl的ItemTemplate属性,相当于给ItemsControl的数据项穿衣服。
- GridViewColumn的CellTemplate属性,相当于给GridViewColumn单元格里的数据穿衣服。
3.2 ControlTemplate
前面说过,
你看到的控件默认形象实际上就是出厂时微软为它穿上的默认服装。
因为ControlTemplate有默认服装,且大部分时候默认服装已经够用了,所以很少有人会手动去修改它。
实际项目中,即使要给ControlTemplate做替换,也往往是用成熟的UI工具包,引入项目中,应用其样式即可。
这很合理,前面也说了,每个控件都是为了实现某种用户操作算法和直观显示某种数据而生的。而这些特定的操作算法是由控件的本质决定的,显然这部分内容较数据来讲是更为固定,交由微软或者专业的组件开发商决定没有问题。
不过,这也并不意味着普通程序员就完全不需要去了解ControlTemplate。因为某些时候,确实也会需要修改它们。
《WPF深入浅出》一书中说到,
实际项目中,ControlTemplate主要由两大用处:
- 通过更换ControlTemplate改变控件外观,使之具有更优的用户体验及外观。
- 接触ControlTemplate,程序员与设计师可以并行工作,程序员可以先用WPF标准控件进行编程,等设计师工作完成后,只需把新的ControlTemplate应用到程序中就可以了。
不过考虑到国内WPF开发的现状,应该很少有设计师和程序员工作完全分离的。所以ControlTemplate的使用上也集中在第一点了。
但第一点,听起来似乎和之前所说的冲突了,ControlTemplate不是呈现算法的吗,怎么变成改变控件外观的了。
其实这并不矛盾,因为控件的外观和控件的本质息息相关。
- Button为什么形状都差不多,一般就是一个椭圆或矩形,可点击,点击后会凹下去。
- TextBox为什么都是一个矩形框,里面可输入文字。
- ScrollBar为什么都是一个长条,你可以拖动它移动。
还记得算法是什么吗?
每本编程入门书都会说,算法是解决问题的办法。
这些控件之所以有这样的外观和交互方式,并不是凭空产生的,而是人设计出来的。显然这样的设计就是为了解决一类问题,即它是一种算法。
当你对控件效果不满意时,就可能需要更改其外观,以贴合实际需求。
此时,我对ControlTemplate又有了一层理解。控件模板描述的是控件外观以及外观对外界刺激所做出的反应(比如各种事件/按钮鼠标触摸后的背景色变化等),而外观和对刺激的反应更深层的含义是该类控件要解决的问题的解决方案。
下面是一个实际开发中的ControlTemplate应用场景,我需要在鼠标移动到按钮上时,按钮的背景色发生变红色(这是为了使鼠标移动到按钮的操作有反馈感):
<Style TargetType="Button"><Style.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border Background="red"><ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/></Border></ControlTemplate></Setter.Value></Setter></Trigger></Style.Triggers>
</Style>
如代码所示,用触发器检验鼠标是否在控件上,若是则修改其控件模板,主要就是将控件模板外的border的背景色变红。
3.3 ContentPresenter
实际上Button的结构就是这么简单,

<Border><ContentPresenter/>
</Border>
一个边框加一个ContentPresenter就组成了一个按钮。
那么,ContentPresenter是什么?
我们可以通过反编译看到其中结构,下面是我整理出来的一个类关系图:

你会发现ContentPresenter的结构和ContentControl高度重合。
这边我也不卖关子,也不想花太多篇幅去深究它们的关系。
你可以把ContentPresenter直接理解成控件的数据内容模板DataTemplate。
有的控件继承了ContentControl,我称之为内容控件。
也有部分控件没有继承它,比如TextBox,

也因此,TextBox的默认控件模板中不存在ContentPresenter。这也很好理解,都没有数据内容,何来数据内容呈现?
你可能会问,那它的Text属性不是数据内容?
当然是,但从控件的角度来看,Text这样的属性已经是相当基础了,就是一段字串(有点类似于“原子数据”),而内容控件的Content可能是有分割必要的,比如Button中可能会有文字描述/图标等。因此我个人倾向于,Text本身这种简单的特性导致了它不作为内容模板出现。
四、结语
写了这么多,主要就是谈了谈对控件以及两个模板的理解。
这种理解更像是WPF开发(也不限于WPF)的内功,不对写代码直接产生效率提升,但会有长远的积极影响。
相关文章:
WPF——Control与Template理解
文章目录 一、前言二、控件三、模板3.1 DataTemplate3.2 ControlTemplate3.3 ContentPresenter 四、结语 一、前言 最近又翻看了下刘铁猛的《深入浅出WPF》,发现对模板章节中的部分内容有了更深的体会,所以写篇文扯扯。 文章标题是Control与Template&a…...
华为HCIA学习(一)
文章目录 一.根据考试题总结知识点(一题一点)二.上午学习三.下午学习四.今天只做了70题,需要的可以找我 一.根据考试题总结知识点(一题一点) 二.上午学习 ① VRP系统是VRP是华为公司从低端到高端的全系列路由器、交换…...
使用jmeter+ant+jenkins+git搭建自动化测试平台
最近正在学习自动化测试,于是随手搭建了一下jmeterantjenkinsgit平台。 接下来,我会按照jdk,jmeter,ant,jenkins,git这个顺序一步一步的搭建起来。 一、jdk。这个就不多说了。我用的是1.8版本的,配环境变…...
C# Winform中在DataGridView中添加Button按钮,操作Button按钮
.Net的DataGridView控件中,提供了一种列的类型,叫 DataGridViewButtonColumn ,这种列类型是展示为一个 按钮,可以给button赋予相应的text,并且,此button可以用来做处理事件的判断依据。 DataGridViewButto…...
Docker 网络学习
docker的网络模式 当你开始大规模使用Docker时,你会发现需要了解很多关于网络的知识。Docker作为目前最火的轻量级容器技术,有很多令人称道的功能,如Docker的镜像管理。然而,Docker同样有着很多不完善的地方,网络方面…...
django创建web服务器
安装 pip install django 创建项目 django-admin startproject report django-admin startapp data //project下可创建多个app 执行使用 python manage.py migrate //orm代码到数据库 python manage.py runserver 0.0.0.0:80 权限管理 python manage.py createsuperuser 创建…...
极光笔记 | 推送服务数据中心选择:合规性与传输效率的双重考量
随着全球化进程的深入,跨境数据传输与存储问题已经变得愈发重要。推送服务的数据中心节点选择不仅关乎数据访问速度和用户体验,同时也直接牵扯到数据合规性和安全保障。EngageLab Push深知这一点,为了满足更多国际客户和全球用户触达需求&…...
Python灰帽编程——初识Python上
1. Python 简介 常用安全工具语言示例perljoomscan whatwebrubymetasploit-frameworkpythonsqlmap pocsuite3gogoby 1.1 Python 起源 1.1.1 语言的作者 贵铎范罗萨姆(Guido van Rossum)荷兰人于1989 年圣诞节始创了python。 大神就是大神࿰…...
OLED透明屏交互技术:开创未来科技的新篇章
OLED透明屏交互技术作为一项前沿的科技创新,正在以其高透明度、触摸和手势交互等特点,引领着未来科技的发展。 不仅在智能手机、可穿戴设备和汽车行业有着广泛应用,还在广告和展示领域展现出巨大的潜力。 那么,尼伽在这篇文章中将…...
揭秘Spring Boot内嵌Tomcat原理
tomcat 介绍 tomcat 是 web容器(servlet 容器),不管请求是访问静态资源HTML、JSP还是java接口,对tomcat而言,都是通过servlet访问: 访问静态资源,tomcat 会交由一个叫做DefaultServlet的类来处…...
分类散点图 stripplot() 加辅助线axhline() 多图合一
分类散点图 stripplot 加辅助线axhline 多图合一 效果图代码 画图没有什么可说的,直接上图 效果图 代码 # 绘制图, 查看是否数值在阈值上 plt.figure(figsize(30, 18)) n 0 for header, value_list in info_dict.items():ref_value_list ref_info_dic…...
一文告诉你为什么时序场景下 TDengine 数据订阅比 Kafka 好
在 TDengine 3.0 中,我们对流式计算、数据订阅功能都进行了再升级,帮助用户极大简化了数据架构的复杂程度,降低整体运维成本。TDengine 提供的类似消息队列产品的数据订阅、消费接口,本质上是为了帮助应用实时获取写入 TDengine 的…...
reg与wire的用法,证明reg可以在右边,wire型在左边,来作组合逻辑处理。
reg与wire的用法,证明reg可以在右边,wire型在左边,来作组合逻辑处理。 1,RTL2,生成的原理图 1,RTL 参考文献: 1,verilog 中 wire 和reg 的使用 2,解决一个assign问题&…...
Studio One6.2简体中文免费最新版本宿主软件
对于一些有创作需求的朋友来说,为自己写的歌制作伴奏是很平常的。今天要和大家分享的就是自己写的歌怎么做伴奏,自己做伴奏的软件有哪些。Studio One是宿主软件界的一个后起之秀,推出的时间不久,但是受到了大量音乐制作人的推崇。…...
算法刷题 week2
目录 week21. 二维数组中的查找题目题解(单调性扫描) O(nm) 2.替换空格题目题解(线性扫描) O(n)(双指针扫描) O(n) 3.从尾到头打印链表题目题解(遍历链表) O(n) week2 1. 二维数组中的查找 题目 题解 (单调性扫描) O(nm) 核心在于发现每个子矩阵右上角的数的性质࿱…...
子网的划分
强化计算机网络发现王道没有这一块的内容,导致做题稀里糊涂。于是个人调研补充。 子网划分是将一个大型IP网络划分成更小的子网,以实现更有效的网络管理和资源分配。 原因: 提高网络性能:子网划分可以减少广播域的大小ÿ…...
Docker安装与卸载
Docker安装与卸载 安装 yum install -y yum-utils \device-mapper-persistent-data \lvm2 --skip-broken更新本地镜像源 打开终端或 SSH 连接到 Rocky Linux 的服务器。 进入 /etc/yum.repos.d/ 目录,该目录包含 Rocky Linux 的 yum 配置文件。 cd /etc/yum.repo…...
【Davinci开发】:开发过程问题记录及总结
开发过程问题总结 1、SWC访问系统OS Timer返回值异常a、代码发现,RTE接口为未连接状态b、连接后,仍然有问题,单步调试,发现没有访问权限当新平台基于之前平台的代码而延续开发时(应用代码相同,但是芯片已经更换),记录开发过程中遇所到的问题,单步调试,逐一排查。 1、…...
数据结构——排序算法——冒泡排序
冒泡排序1 void swap(vector<int> arr, int i, int j) {int temp arr[i];arr[i] arr[j];arr[j] temp;}void bubbleSort1(vector<int> arr) {for (int i 0; i < arr.size() - 1; i){for (int j 0; j < arr.size() - 1 - i; j){if (arr[j] > arr[j 1…...
vscode使用
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:…...
基于MCP协议实现AI助手个性化:Terminal Buddies项目实战解析
1. 项目概述:当你的终端伙伴遇见AI助手 如果你和我一样,每天有大量时间泡在终端和代码编辑器里,那么一个能带来些许乐趣和陪伴感的“数字伙伴”或许能点亮枯燥的编码时光。Terminal Buddies 正是这样一个巧妙结合了复古 ASCII 艺术、轻量级游…...
Steam成就管理终极指南:三步掌握高效成就解锁技巧
Steam成就管理终极指南:三步掌握高效成就解锁技巧 【免费下载链接】SteamAchievementManager A manager for game achievements in Steam. 项目地址: https://gitcode.com/gh_mirrors/st/SteamAchievementManager Steam Achievement Manager(SAM&…...
MATLAB 2018a/2023b实测:Libsvm安装后如何用自带数据集快速验证与跑通第一个模型
MATLAB 2018a/2023b实战:Libsvm安装后快速验证与模型跑通全流程 当你第一次在MATLAB中成功安装Libsvm后,那种兴奋感可能很快会被"接下来该做什么"的迷茫所取代。别担心,这篇文章将带你用Libsvm自带的heart_scale数据集,…...
【Midjourney水墨风创作终极指南】:20年AI视觉专家亲授7大不可外传的Ink Wash参数配方与避坑清单
更多请点击: https://intelliparadigm.com 第一章:水墨风AI创作的认知革命与历史语境 水墨艺术承载着东方哲学中“虚实相生”“气韵生动”的深层认知范式,而当生成式AI介入水墨风格建模时,其本质并非简单纹理迁移,而是…...
ClaudeCode入门08-Git配合(小白入门:不知道怎么写Git提交记录?让AI自动帮你写好)
🎯 本文目标 学会用 Claude Code 自动化 Git 工作流:自动写 Commit Message、管理分支、处理冲突。 😰 Git 新手的痛点 git commit -m "fix" git commit -m "update" git commit -m "修改了一些东西" 不知道 Conventional Commits 是什么 …...
在MacBook Pro上构建工业物联网数据采集:libmodbus实战指南
1. 为什么选择MacBook Pro作为工业物联网开发平台 工业物联网开发通常需要频繁的现场调试和设备对接,传统工控机笨重且不便携。MacBook Pro凭借其出色的性能表现和稳定的macOS系统,正在成为工程师们的新宠。我去年参与一个智慧农业项目时,就深…...
Ile-Ser-Bradykinin(T-Kinin) ;ISRPPGFSPFR
一、基础信息多肽名称:Ile-Ser-Bradykinin,别名 T-Kinin(T - 激肽) 三字母序列:Ile-Ser-Arg-Pro-Pro-Gly-Phe-Ser-Pro-Phe-Arg 单字母序列:ISRPPGFSPFR 氨基酸数量:11 aa 结构修饰:线…...
ROS机器人开发:用tf_monitor和tf_echo快速诊断你的坐标转换问题(附真实案例)
ROS机器人坐标转换问题诊断实战:从工具使用到思维升级 当机器人的激光雷达数据与地图匹配出现偏移,或者机械臂末端执行器总是偏离目标位置几厘米时,有经验的开发者会第一时间检查坐标转换系统。ROS中的tf库虽然强大,但一旦出现问题…...
CompressO终极指南:免费开源视频图片压缩工具完整使用教程
CompressO终极指南:免费开源视频图片压缩工具完整使用教程 【免费下载链接】compressO Convert any video/image into a tiny size. 100% free & open-source. Available for Mac, Windows & Linux. 项目地址: https://gitcode.com/gh_mirrors/co/compres…...
Win11 22H2 打不开 IE?亲测有效!一行代码直接调出独立 IE 窗口
很多升级到 Windows 11 22H2 的用户都遇到过这样的困扰:明明银行、政务、企业内网等旧系统明确要求用 IE 浏览器登录,可系统里找不到 IE 入口,Edge 的 IE 兼容模式又频繁失效,直接打开 IE 还会强制跳转到 Edge,折腾半天…...
