詳解WPF中VisualTree增加Visual的處理方法
這里我們將介紹WPF中VisualTree增加Visual的處理方法,首先我們將從一個典型的問題開始講述。希望這些討論能對大家有所幫助。
作為一個WPF控件開發(fā)者,我在工作中經(jīng)常遇到如本文標題所示的問題。其實,這個問題并不是很難,只是在操作上有些繁瑣。本文將嘗試對這個問題進行解答,并且對相關的一些技術細節(jié)加以探討。
#T#
先從我遇到的一個典型的問題開始吧:寫一個MyElement類,要求如下:
從FrameworkElement繼承
增加一個Button到它的VisualTree上
在Visual上有一個AddVisualChild方法,相信很多剛接觸這個方法的同學們(好吧,至少我是這樣)都會“顧名思義”地認為這個方法就可以解決本文的問題。再加上MSDN上也給出了一個例子來“火上澆油”一把。于是,一陣竊喜之后,我興奮地敲出了以下代碼:
- Code
- class MyElement : FrameworkElement
- {
- private Button _button = new Button() { Content = "I'm a Button!"};
- public MyElement()
- {
- this.AssembleVisualChildren();
- }
- private void AssembleVisualChildren()
- {
- this.AddVisualChild(this._button);
- }
- protected override int VisualChildrenCount
- {
- get
- {
- return 1;
- }
- }
- protected override Visual GetVisualChild(int index)
- {
- return this._button ;
- }
- }
然后將這個MyElement加入測試窗口,代碼如下:
- Code
- <Window
- x:Class="AddVisualChildTest.Window1"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:loc="clr-namespace:AddVisualChildTest"
- WindowStartupLocation="CenterScreen"
- Title="Window1" Height="300" Width="300">
- <Grid>
- <loc:MyElement Margin="10"/>
- </Grid>
- </Window>
運行后的結(jié)果如下:
空空如也!嗯,被忽悠了。一陣失落、打擊之后,我的好奇心被激發(fā)了:這是為什么呢?于是我狂找資料,終于被我發(fā)現(xiàn)了:
實際上,在上面這個例子中,AddVisualChild這個方法只是在MyElement和Button之間建立起了一種VisualTree上的父子關系,但是并沒有將Button掛接到MyElement的VisualTree上,所以最終我們沒有在屏幕上看到這個Button。
為了將Button真正掛接到MyElement的VisualTree上,還需要額外做一件事情:在VisualTree上為這個Button分配空間并且指定位置,這個過程叫做Layout。此過程分兩個部分:一個是Measure,另一個是Arrange。這兩個過程在FrameworkElement上對應著兩個方法:MeasureOverride和ArrangeOverride方法。具體做法如下:
- Code
- protected override Size MeasureOverride(Size availableSize)
- {
- if (this.VisualChildrenCount > 0)
- {
- UIElement child = this.GetVisualChild(0) as UIElement;
- Debug.Assert(child != null); // !Assert
- child.Measure(availableSize);
- return child.DesiredSize;
- }
- return availableSize;
- }
- protected override Size ArrangeOverride(Size finalSize)
- {
- Rect arrangeRect = new Rect()
- {
- Width = finalSize.Width,
- Height = finalSize.Height
- };
- if (this.VisualChildrenCount > 0)
- {
- UIElement child = this.GetVisualChild(0) as UIElement;
- Debug.Assert(child != null); // !Assert
- child.Arrange(arrangeRect);
- }
- return finalSize;
- }
再次運行程序:
目標實現(xiàn)。
由此,我們可以總結(jié)出這個問題的解決方案如下:
在MyElement的構(gòu)造器中調(diào)用AddVisualChild方法;
重寫VisualChildCount屬性;
重寫GetVisualChild方法;
重寫MeasureOverride方法;
重寫ArrangeOverride方法;
另外,WPF在此問題的解決上也為開發(fā)者提供了一些必要的幫助。就我所知的,有如下幾個內(nèi)容:
1、Panel
還是本文開始提到的問題,只不過要將其中的FrameworkElement換為Panel。除了上面所提到的方法,Panel為我們提供了更加方便的實現(xiàn)方式。代碼如下:
- Code
- class MyElement : Panel
- {
- private Button _button = new Button() { Content = "I'm a Button!" };
- public MyElement()
- {
- this.Children.Add(_button);
- }
- protected override Size MeasureOverride(Size availableSize)
- {
- if (this.VisualChildrenCount > 0)
- {
- UIElement child = this.GetVisualChild(0) as UIElement;
- Debug.Assert(child != null); // !Assert
- child.Measure(availableSize);
- return child.DesiredSize;
- }
- return availableSize;
- }
- protected override Size ArrangeOverride(Size finalSize)
- {
- Rect arrangeRect = new Rect()
- {
- Width = finalSize.Width,
- Height = finalSize.Height
- };
- if (this.VisualChildrenCount > 0)
- {
- UIElement child = this.GetVisualChild(0) as UIElement;
- Debug.Assert(child != null); // !Assert
- child.Arrange(arrangeRect);
- }
- return finalSize;
- }
- }
之所以能這樣做的原因是Panel已經(jīng)替我們將如下幾個工作封裝在了UIElementCollection(Panel的Children屬性)中:
AddVisualChild
VisualChildCount
GetVisualChild
2、VisualCollection
另外,在這個過程中,我們還可以使用一個叫做VisualCollection的類來作為所有 Visual Child的容器。這個容器構(gòu)造的時候需要一個Visual類型的Parent,然后在添加、刪除Visual Child的時候,它的相應方法(Add,Remove)就會幫助我們自動調(diào)用Parent的AddVisualChild和RemoveVisualChild方法。如此一來,我們的工作量又減少了。具體的實現(xiàn)代碼很簡單,這里就不貼了(總得動動腦子是不?)。
原文標題:WPF:如何在VisualTree上增加Visual?
鏈接:http://www.cnblogs.com/AaronLu/archive/2009/11/09/1599348.html