详解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,相信不僅能夠帶來一些出眾的效果,也能所軟件的架構和模塊更加明晰。
轉載于:https://www.cnblogs.com/xpvincent/p/3695631.html
總結
以上是生活随笔為你收集整理的详解Adorner Layer(zz)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [OpenJudge] 百练2754 八
- 下一篇: FZU 2171(线段树的延迟标记)