详解Adorner Layer(zz)
首先,千萬不要覺得Adorner離你很遠(yuǎn),因為最簡單的WPF界面也會用到Adorner。在WPF中,下面的幾個很常見的功能,都是用Adorner實現(xiàn)的。
??? 1. 光標(biāo)(caret)
??? 2. 焦點(focus)
??? 3. 高亮(highlight)
??? 4. 拖拽預(yù)覽(drag and drop)
??? 5. 拼寫錯誤提示
??? 6. 數(shù)據(jù)綁定中用來提示錯誤的Error Template
當(dāng)然還有別的,用Reflector很容易找到一些WPF中自帶的Adorner。如下圖所示。
很多吧!這些Adorner都是放在一個叫Adorner Layer的層上。MSDN解釋說Adorner Layer是置于一個窗口內(nèi)所有其它控件之上的。而且AdornerLayer類又沒有public的結(jié)構(gòu)函數(shù),只能用下面的代碼來取得某個控件的Adorner Layer的實例:
但是上面的方式會讓人產(chǎn)生兩個錯覺:
??? 1. Adorner Layer是WPF自帶的,內(nèi)置的,我們管不了。
??? 2. 每個控件都有自己的Adorner Layer。
如果繼續(xù)看看GetAdornerLayer的代碼,就很容易知道Adorner Layer是從何而來的了。鑒于這個函數(shù)的源代碼比較丑陋,就不貼在這里給微軟丟人了。但是從源代碼我們可以知道:
??? 1. 不是每個控件都有Adorner Layer,其實只有AdornerDecorator和ScrollContentPresenter附帶有Adorner Layer。
??? 2. 對某個element取到的Adorner Layer,一般是其Ancestor的。
順便解釋一下,Decorator,很熟悉吧,就是裝飾模式在WPF中的產(chǎn)物。是個比Adorner更寬泛的東西,Adorner就是Decorator的一種。我們常見的Border和 Viewbox等都屬于Decorator。關(guān)于Decorator的更多信息可以看這里。
這樣說來,AdornerDecorator就是Adorner Layer的提供者,我們再來看一下Window的默認(rèn)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>也就是說所有的使用默認(rèn)Template的Window都會有一個AdornerDecorator為其提供Adorner Layer。所以如果要自定義Window的Template也一定要記得為ContentPresenter加一個AdornerDecorator。
下面是AdornerDecorator的ArrangeOverride函數(shù)定義:
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之上了呢?請讀者自己想一想吧。
這里我也很想啰嗦一下,在重寫的函數(shù)里,要不要調(diào)用base函數(shù)?在什么地方調(diào)用?都是很值得注意的。這個問題是要看具體情況具體分析的。而要想正確地調(diào)用base函數(shù),就要對基類有一定了解。所以在需要的時候閱讀源代碼,了解其工作原理,是很有必要的。這個公理,也可以引出這樣一個推論——在短時間內(nèi)學(xué)通一項技術(shù)的想法或產(chǎn)品,都是不現(xiàn)實的。(學(xué)通的定義:如果把WPF從.NET里刪除,理論上,能夠自己重寫一套出來)
知道了Adorner Layer的來源,我們再來看一下Adorner Layer上的Adorner。Adorner Layer里只能放Adorner,而Adorner也沒有無參構(gòu)造函數(shù)。所以有關(guān)Adorner的一切操作,在默認(rèn)情況下,都只能在C#代碼中進(jìn)行(當(dāng)然總有辦法在XAML中定義Adorner)。在構(gòu)造Adorner的時候,必須把Adorned Element做為參數(shù)傳給Adorner。因為Adorner需要知道Adorned Element的位置和大小等信息。以便讓Adorner Layer知道在什么地方渲染這個Adorner。所有的這些信息由Adorner Layer保存在一個叫Adorner Info的內(nèi)部類中。
其中包含了渲染每個Adorner時所需要的一些信息。其中RenderSize和Transform都是根據(jù)Adorner的AdornedElement計算出來的。一但某個UIElement的位置或Transform等發(fā)生了變化。這個Element所關(guān)聯(lián)到的所有的Adorner的AdornerInfo都會被更新一次。這樣Adorner看上去和Adorned Element是一個控件一樣,其實只是用Trasform把二者從位置關(guān)系上粘在了一起而已。
上面算是把Adorner Layer的原理介紹完了。簡言之,一般一個Window有唯一的一個Adorner Layer,在渲染時,所有的Adorner都被放在了窗口的左上角,再用RenderTransform把這個Adorner移動到其關(guān)聯(lián)的Adorned Element上。
Adorner繼承于FrameworkElement,是個連Content屬性,Child屬性或是ControlTemplate屬性都沒有的東西。后果就是你要自己做一個Adorner,就要先繼承Adorner類,再重寫OnRender函數(shù),并在里面,一條線,一條線地畫出你想要的效果。當(dāng)然誰也不想真的用DrawingContext是畫個東西出來,解決方案也有不少。一個就是先給Adorner加上個UIElement類型的Child屬性,然后就可以住這個UIElementAdorner里加WPF常見控件了不是?這里也給出了實現(xiàn)方式。
最后一個問題就是應(yīng)該在什么時候用Adorner?我想應(yīng)該從其設(shè)計理念上來回答這個問題。Adorner本身屬于Decorator的一種,能夠在不改變原有XAML結(jié)構(gòu)的條件下,提供為每個獨立控件,附著其它界面元素或裝飾物的手段。這是一個很強(qiáng)大的設(shè)計思想。至于你可以用它來做什么?從第一張圖中你應(yīng)該可以看出一些端倪。但是在實際項目中,還要看大家自己的發(fā)揮了。下面大致給大家列舉一些例子。(微軟已經(jīng)實現(xiàn)的就不再例出來了)
??? 1. ListView列頭中,表示當(dāng)前排序方式的小箭頭。我在之前的文章中已經(jīng)給出了實現(xiàn)方式。
??? 2. 圖表中,如果需要每點一下鼠標(biāo),可以在圖表上留下一個標(biāo)記。這個標(biāo)記就可以放在Adorner Layer中。
??? 3. 程序加載或某個操作進(jìn)行中時,為整個界面加的蒙版與Processing動畫。
??? 4. 界面設(shè)計工具中,用來調(diào)節(jié)控件大小的錨點。
這里已經(jīng)給出了實現(xiàn)方式。
??? 5. 放大鏡控件中,放大出來的圖像。(沒實例,想象中)
??? 6. 鼠標(biāo)拖出來的選框,這個用圖描述比較簡潔。
在WPF程序中,善用Adorner Layer,相信不僅能夠帶來一些出眾的效果,也能所軟件的架構(gòu)和模塊更加明晰。
轉(zhuǎn)載于:https://www.cnblogs.com/xpvincent/p/3695631.html
總結(jié)
以上是生活随笔為你收集整理的详解Adorner Layer(zz)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [OpenJudge] 百练2754 八
- 下一篇: FZU 2171(线段树的延迟标记)