详解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理解上在看过博客园各个大神 ...
随机推荐
- jvm内存模型和内存分配
1.什么是jvm? (1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的. (2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和 ...
- Stanford机器学习---第一讲. Linear Regression with one variable
原文:http://blog.csdn.net/abcjennifer/article/details/7691571 本栏目(Machine learning)包括单参数的线性回归.多参数的线性回归 ...
- HDU2546(01背包饭卡)
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够).所以大家 ...
- PhpStorm主题
图的github仓库有很多编辑器的主题,jetbrains目录下都是PhpStorm支持的主题 1.到http://daylerees.github.io/预览各个主题的风格,找到自己喜欢的: 2.在 ...
- linux 下查看某个端口是否被占用
lsof -i:端口号 转自: http://my.oschina.net/u/193184/blog/146885
- 【云计算】Dockerfile、镜像、容器快速入门
Dockerfile.镜像.容器快速入门 1.1.Dockerfile书写示例 Dockerfile可以用来生成Docker镜像,它明确的定义了Image的生成过程.虽然直接修改容器也可以提交生成镜像 ...
- iOS的 context 和Android 中的 canvas
ios 想要绘图,要用到CGContextRef类.最基本的用法是在- (void)drawRect:(CGRect)rect 函数中绘制. Android 中要用到Canvas类.最基本的用法是在 ...
- HDU 1087 Super Jumping! Jumping! Jumping! 最大递增子序列
Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 ...
- CodeForces - 404A(模拟题)
Valera and X Time Limit: 1000MS Memory Limit: 262144KB 64bit IO Format: %I64d & %I64u Submit ...
- Eclipse中android工程C++文件中出现的莫名其妙的错误
大多数是std库相关的问题,例如 vector<int> v; v.push_back(23);//这句语法是没有错误的,但是每次执行Run As的时候就会报错 尝试1:在工程名右键-Cl ...