详解Adorner Layer(zz)
首先,千万不要觉得Adorner离你很远,因为最简单的WPF界面也会用到Adorner。在WPF中,下面的几个很常见的功能,都是用Adorner实现的。
1. 光标(caret)
2. 焦点(focus)
3. 高亮(highlight)
4. 拖拽预览(drag and drop)
5. 拼写错误提示
6. 数据绑定中用来提示错误的Error Template
当然还有别的,用Reflector很容易找到一些WPF中自带的Adorner。如下图所示。
很多吧!这些Adorner都是放在一个叫Adorner Layer的层上。MSDN解释说Adorner Layer是置于一个窗口内所有其它控件之上的。而且AdornerLayer类又没有public的结构函数,只能用下面的代码来取得某个控件的Adorner Layer的实例:
但是上面的方式会让人产生两个错觉:
1. Adorner Layer是WPF自带的,内置的,我们管不了。
2. 每个控件都有自己的Adorner Layer。
如果继续看看GetAdornerLayer的代码,就很容易知道Adorner Layer是从何而来的了。鉴于这个函数的源代码比较丑陋,就不贴在这里给微软丢人了。但是从源代码我们可以知道:
1. 不是每个控件都有Adorner Layer,其实只有AdornerDecorator和ScrollContentPresenter附带有Adorner Layer。
2. 对某个element取到的Adorner Layer,一般是其Ancestor的。
顺便解释一下,Decorator,很熟悉吧,就是装饰模式在WPF中的产物。是个比Adorner更宽泛的东西,Adorner就是Decorator的一种。我们常见的Border和 Viewbox等都属于Decorator。关于Decorator的更多信息可以看这里。
这样说来,AdornerDecorator就是Adorner Layer的提供者,我们再来看一下Window的默认Template。取自Blend中的aero.normalcolor.xaml文件。
<ControlTemplate TargetType="{x:Type Window}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</ControlTemplate>
也就是说所有的使用默认Template的Window都会有一个AdornerDecorator为其提供Adorner Layer。所以如果要自定义Window的Template也一定要记得为ContentPresenter加一个AdornerDecorator。
下面是AdornerDecorator的ArrangeOverride函数定义:
protected override Size ArrangeOverride(Size finalSize)
{
Size size = base.ArrangeOverride(finalSize);
if (VisualTreeHelper.GetParent(this._adornerLayer) != null)
{
this._adornerLayer.Arrange(new Rect(finalSize));
}
return size;
}
在base.ArrangeOverride中,Window中的Content得以渲染。然后AdornerDecorator才去Arrange自带的_adornerLayer。这样这个Adorner layer就位于所有Window Content之上了。
但是,是不是说就没有办法把东西放在AdornerLayer之上了呢?请读者自己想一想吧。
这里我也很想啰嗦一下,在重写的函数里,要不要调用base函数?在什么地方调用?都是很值得注意的。这个问题是要看具体情况具体分析的。而要想正确地调用base函数,就要对基类有一定了解。所以在需要的时候阅读源代码,了解其工作原理,是很有必要的。这个公理,也可以引出这样一个推论——在短时间内学通一项技术的想法或产品,都是不现实的。(学通的定义:如果把WPF从.NET里删除,理论上,能够自己重写一套出来)
知道了Adorner Layer的来源,我们再来看一下Adorner Layer上的Adorner。Adorner Layer里只能放Adorner,而Adorner也没有无参构造函数。所以有关Adorner的一切操作,在默认情况下,都只能在C#代码中进行(当然总有办法在XAML中定义Adorner)。在构造Adorner的时候,必须把Adorned Element做为参数传给Adorner。因为Adorner需要知道Adorned Element的位置和大小等信息。以便让Adorner Layer知道在什么地方渲染这个Adorner。所有的这些信息由Adorner Layer保存在一个叫Adorner Info的内部类中。
其中包含了渲染每个Adorner时所需要的一些信息。其中RenderSize和Transform都是根据Adorner的AdornedElement计算出来的。一但某个UIElement的位置或Transform等发生了变化。这个Element所关联到的所有的Adorner的AdornerInfo都会被更新一次。这样Adorner看上去和Adorned Element是一个控件一样,其实只是用Trasform把二者从位置关系上粘在了一起而已。
上面算是把Adorner Layer的原理介绍完了。简言之,一般一个Window有唯一的一个Adorner Layer,在渲染时,所有的Adorner都被放在了窗口的左上角,再用RenderTransform把这个Adorner移动到其关联的Adorned Element上。
Adorner继承于FrameworkElement,是个连Content属性,Child属性或是ControlTemplate属性都没有的东西。后果就是你要自己做一个Adorner,就要先继承Adorner类,再重写OnRender函数,并在里面,一条线,一条线地画出你想要的效果。当然谁也不想真的用DrawingContext是画个东西出来,解决方案也有不少。一个就是先给Adorner加上个UIElement类型的Child属性,然后就可以住这个UIElementAdorner里加WPF常见控件了不是?这里也给出了实现方式。
最后一个问题就是应该在什么时候用Adorner?我想应该从其设计理念上来回答这个问题。Adorner本身属于Decorator的一种,能够在不改变原有XAML结构的条件下,提供为每个独立控件,附着其它界面元素或装饰物的手段。这是一个很强大的设计思想。至于你可以用它来做什么?从第一张图中你应该可以看出一些端倪。但是在实际项目中,还要看大家自己的发挥了。下面大致给大家列举一些例子。(微软已经实现的就不再例出来了)
1. ListView列头中,表示当前排序方式的小箭头。我在之前的文章中已经给出了实现方式。
2. 图表中,如果需要每点一下鼠标,可以在图表上留下一个标记。这个标记就可以放在Adorner Layer中。
3. 程序加载或某个操作进行中时,为整个界面加的蒙版与Processing动画。
4. 界面设计工具中,用来调节控件大小的锚点。
这里已经给出了实现方式。
5. 放大镜控件中,放大出来的图像。(没实例,想象中)
6. 鼠标拖出来的选框,这个用图描述比较简洁。
在WPF程序中,善用Adorner Layer,相信不仅能够带来一些出众的效果,也能所软件的架构和模块更加明晰。
详解Adorner Layer(zz)的更多相关文章
- 如何在真机上调试Android应用程序(图文详解)(zz)
http://www.cnblogs.com/lanxuezaipiao/archive/2013/03/11/2953564.html 1.首先将手机设置为调试模式 方法:设置——应用程序——开 ...
- 详解BLE 空中包格式—兼BLE Link layer协议解析
BLE有几种空中包格式?常见的PDU命令有哪些?PDU和MTU的区别是什么?DLE又是什么?BLE怎么实现重传的?BLE ACK机制原理是什么?希望这篇文章能帮你回答以上问题. 虽然BLE空中包(pa ...
- zz:NETCONF协议详解
随着SDN的大热,一个诞生了十年之久的协议焕发了第二春,它就是NETCONF协议.如果你在两年前去搜索NETCONF协议,基本得到的信息都是"这个协议是一个网管协议,主要目的是弥补SNMP协 ...
- zz详解深度学习中的Normalization,BN/LN/WN
详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...
- 【ZZ】详解哈希表的查找
详解哈希表的查找 https://mp.weixin.qq.com/s/j2j9gS62L-mmOH4p89OTKQ 详解哈希表的查找 2018-03-01 算法与数据结构 来自:静默虚空 http: ...
- KMP字符串模式匹配详解(zz)
刚看到位兄弟也贴了份KMP算法说明,但本人觉得说的不是很详细,当初我在看这个算法的时候也看的头晕昏昏的,我贴的这份也是网上找的.且听详细分解: KMP字符串模式匹配详解 来自CSDN A_B_ ...
- <ZZ>linux yum命令详解
http://www.cnblogs.com/chuncn/archive/2010/10/17/1853915.html yum(全称为 Yellow dog Updater, Modified)是 ...
- NAND_FLASH_内存详解与读写寻址方式
一.内存详解 NAND闪存阵列分为一系列128kB的区块(block),这些区块是 NAND器件中最小的可擦除实体.擦除一个区块就是把所有的位(bit)设置为"1"(而所有字节(b ...
- JavaScript事件详解-Zepto的事件实现(二)【新增fastclick阅读笔记】
正文 作者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致.此文的fastclick理解上在看过博客园各个大神 ...
随机推荐
- 磁盘 I/O 性能监控指标和调优方法
在介绍磁盘 I/O 监控命令前,我们需要了解磁盘 I/O 性能监控的指标,以及每个指标的所揭示的磁盘某方面的性能.磁盘 I/O 性能监控的指标主要包括:指标 1:每秒 I/O 数(IOPS 或 tps ...
- 以Python角度学习Javascript(二)之DOM
HTML DOM 定义了访问和操作 HTML 文档的标准方法. DOM 将 HTML 文档表达为树结构. 文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文 ...
- 二模Day2题解
小明搬家 题目描述 小明要搬家了,大家都来帮忙. 小明现在住在第N楼,总共K个人要把X个大箱子搬上N楼. 最开始X个箱子都在1楼,但是经过一段混乱的搬运已经乱掉了.最后大家发现这样混乱地搬运过程效率太 ...
- shell笔记-local、export用法 、declare、set
local一般用于局部变量声明,多在在函数内部使用. 1. Shell脚本中定义的变量是global的,其作用域从被定义的地方开始,到shell结束或被显示删除的地方为止. 2. ...
- centos7 安装kvm, 并创建虚拟机
# yum –y install qemu-kvm qemu-img bridge-utils # yum –y install libvirt virt-install virt-manager # ...
- 【转】solr+ajax智能拼音详解---solr跨域请求
本文转自:http://blog.csdn.net/wangzhaodong001/article/details/8529090 最近刚做完solr的ajax智能拼音.总结一下. 前端:jQuery ...
- codeforces 471B. MUH and Important Things 解题报告
题目链接:http://codeforces.com/problemset/problem/471/B 题目意思:有 n 个 tasks,编号依次为 1 - n,每个 task 都有一定的难度值来评估 ...
- java关闭流,解压缩后的清除
关闭流文件和file文件的时候,先打开的后关闭,后打开的先关闭,实在不行调用system.jc()方法
- 【leetcode】clone-graph
写在前面的话: 看了看自己的博客,从一月底开始就没怎么更新过,我也确实将近5个月没怎么写代码了.今天突然觉得有些心慌,感觉手都已经生疏了.果然,随便找了道题就卡住了.隐约感觉要用map但又不太记得用法 ...
- Ubuntu 更新源
1.首先备份Ubuntu12.04源列表 sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup (备份下当前的源列表) 2.修改更新源 ...