首先,千万不要觉得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)的更多相关文章

  1. 如何在真机上调试Android应用程序(图文详解)(zz)

    http://www.cnblogs.com/lanxuezaipiao/archive/2013/03/11/2953564.html   1.首先将手机设置为调试模式 方法:设置——应用程序——开 ...

  2. 详解BLE 空中包格式—兼BLE Link layer协议解析

    BLE有几种空中包格式?常见的PDU命令有哪些?PDU和MTU的区别是什么?DLE又是什么?BLE怎么实现重传的?BLE ACK机制原理是什么?希望这篇文章能帮你回答以上问题. 虽然BLE空中包(pa ...

  3. zz:NETCONF协议详解

    随着SDN的大热,一个诞生了十年之久的协议焕发了第二春,它就是NETCONF协议.如果你在两年前去搜索NETCONF协议,基本得到的信息都是"这个协议是一个网管协议,主要目的是弥补SNMP协 ...

  4. zz详解深度学习中的Normalization,BN/LN/WN

    详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...

  5. 【ZZ】详解哈希表的查找

    详解哈希表的查找 https://mp.weixin.qq.com/s/j2j9gS62L-mmOH4p89OTKQ 详解哈希表的查找 2018-03-01 算法与数据结构 来自:静默虚空 http: ...

  6. KMP字符串模式匹配详解(zz)

    刚看到位兄弟也贴了份KMP算法说明,但本人觉得说的不是很详细,当初我在看这个算法的时候也看的头晕昏昏的,我贴的这份也是网上找的.且听详细分解: KMP字符串模式匹配详解 来自CSDN     A_B_ ...

  7. <ZZ>linux yum命令详解

    http://www.cnblogs.com/chuncn/archive/2010/10/17/1853915.html yum(全称为 Yellow dog Updater, Modified)是 ...

  8. NAND_FLASH_内存详解与读写寻址方式

    一.内存详解 NAND闪存阵列分为一系列128kB的区块(block),这些区块是 NAND器件中最小的可擦除实体.擦除一个区块就是把所有的位(bit)设置为"1"(而所有字节(b ...

  9. JavaScript事件详解-Zepto的事件实现(二)【新增fastclick阅读笔记】

    正文 作者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致.此文的fastclick理解上在看过博客园各个大神 ...

随机推荐

  1. opencv中,c和c++版本区别体验

    参考:http://www.cnblogs.com/tornadomeet/archive/2012/04/29/2476277.html

  2. Java字符串split函数的注意事项

    Java字符串的split方法可以分割字符串,但和其他语言不太一样,split方法的参数不是单个字符,而是正则表达式,如果输入了竖线(|)这样的字符作为分割字符串,会出现意想不到的结果, 如, Str ...

  3. jQuery mobile 开发问题记录

    一.动态加载页面问题 1.存在这样一个页面布局: main.html 为主界面A,B为该页面中的三个page,其中A为splitview左部分页面,B为右半部页面 a1.html 为一个独立的页面 a ...

  4. 【Django】Django 如何支持 分组查询、统计?

    代码: from django.db.models import Sum alarm_sum_group_items = models.FILE_PROTECT_ALARM.objects.filte ...

  5. hdu3555

    基本的数位dp #include <cstdio> #include <cstring> using namespace std; #define D(x) x ; long ...

  6. iOS 用CALayer实现动画

    与动画有关的几个类的继承关系 涉及到动画的类主要有6个,看一下它们的基本用途: 1. CAAnimation  动画基类 2. CAAnimationGroup 组合多个动画 3. CAPropert ...

  7. linux下重启tomcat、实时查看tomcat运行日志

    在Linux系统下,重启Tomcat使用命令操作的! 首先,进入Tomcat下的bin目录 cd /usr/local/tomcat/bin 使用Tomcat关闭命令 ./shutdown.sh 查看 ...

  8. HDU1267 递推

    下沙的沙子有几粒? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  9. [Android Pro] 利用tcpdump和wireshark对android网络请求进行分析

    一: tcpdump操作流程 1. 手机要有root权限 2. 下载tcpdump   http://www.strazzere.com/android/tcpdump 3. adb push c:\ ...

  10. iphone越狱还原

    在Cydia 里安装Impactor 就行了 .在操作时需要全程联网: .请至少保证 % 的电量以防止在恢复过程出现断电的情况(建议将设备连接至电源): .设备将恢复至出厂状态,所有用户数据都将被清除 ...