WPF Panel的性能分析


  大家知道WPF有多種Panel,如Canvas,Grid,StackPanel,DockPanel,WrapPanel,VirtualizingPanel等。

在一些場景下可以選擇任何一種或多種Panel實現一種效果。本文談一下在同一場景下使用哪種Panel性能會更好。

  新建一個WPF項目,各放置Stackpanel,Canvas,Grid,看下所占的內存,如圖1,2,3

                     圖1 Grid

                    圖2 Canvas

                    圖3 StackPanel

我們可以看到空內容情況下內存容量是StackPanel=Canvas<Grid。

查看Panel類的OnRender方法,各類面板的性能差異主要體現在Render的過程中計算(Measure)排列(Arrange)不同容器內容的功能差異導致的性能消耗。

大家有興趣可以反編譯.NET Framework,閱讀下StackPanel,Canvas,Grid各自的MeasureOverride和ArrangeOverride方法。

1.Measure和Arrange:

  在MeasureOverride方法里,影響性能的是自適應Arrange的屬性。舉例:HorizontalAlignment.Strech或Grid中ColumnDefinition的Width="Auto"。

只要設置了這些屬性,則Panel控件的子控件都將會拉伸或者自動計算大小。

  在ArrangeOverride方法里,影響性能的是不同的子控件在Panel位置之間的相互作用的復雜度以及子控件的數目。

2.Panel類的派生

  在項目中有些特殊需求WPF現有提供的布局控件滿足不了需求,這時候需要我們寫自定義控件。舉例:在ERP應用軟件中大量的數據圖表需要在UI顯示,這時候

我們需要在布局中顯示類似HTML中TABLE 百分比的功能。兩種做法:1.Binding副控件的實際大小通過Converter計算百分比 2.派生自布局控件重寫功能

如果使用方法1通過默認控件Binding,通過Memory Profiler看到其性能表現相當糟糕。

使用方法2.繼承自布局控件,其性能消耗小了很多。

 

下面我們分別看下Grid,Canvas,StackPanel

1.Grid:

  Grid定義一個可設置的的網格區域,可以將該網格區域分割成多行與多列。

  如果使用按比例(如:3*,7*)或者Auto調節行列大小,Grid 是一個性能損耗最嚴重的面板控件。

  原因是:當VisualTree上Child的原始大小和布局位置通過 Grid 來指定的時候,Child的區域大小計算非常復雜。同時,在所有Panel類型控件中,它的布局過程是最復雜的。

  性能評估:它的計算性能和排列性能屬於中低水平。

2.Canvas:

  Canvas定義了一個區域內的坐標系,Child可根據該坐標系決定處於布局中的絕對位置。

  Canvas 擁有在所有控件中最好的排列(Arrange)性能,在計算(Measure)步驟中也有很好的性能表現。

  原因是:針對Arrange,Canvas的所有Child位置都是絕對位置,是固定,直接指定的,Canvas並沒有拉伸(Strech,Uriform,Fill etc...)的屬性,所有Child都是使用自己的原始尺寸。

  性能評估:性能最好,無論是計算性能和排列性能。

3.StackPanel:

  StackPanel定義了區域內的Child將按照水平方向或垂直方向排列成一行。

  在 StackPanel 內,Child的尺寸將如此計算:根據 StackPanel 的排列(Orientation)方向,如:垂直方向,則它的Child在水平方向的尺寸則使用原始尺寸或相對尺寸,而垂直方向的尺寸則使用原始尺寸(對齊屬性在此方向並不影響它的尺寸)。由於它的排列(Arrange)步驟相對簡單,只是將Child按順序的逐個排列,所以它在這步驟的性能在所有Panel控件中排前列。

  性能評估:計算(Measure)性能屬於中等水平,排列(Arrange)性能屬於高等水平。

代碼:

 1 <Window x:Class="PerformanceDemo.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow" Height="350" Width="525">
 5     <!--<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
 6         <Grid.RowDefinitions>
 7             <RowDefinition Height="2*"></RowDefinition>
 8             <RowDefinition Height="Auto"></RowDefinition>
 9             <RowDefinition Height="Auto"></RowDefinition>
10             <RowDefinition Height="Auto"></RowDefinition>
11         </Grid.RowDefinitions>
12         <Grid.ColumnDefinitions>
13             <ColumnDefinition Width="3*"></ColumnDefinition>
14             <ColumnDefinition Width="Auto"></ColumnDefinition>
15         </Grid.ColumnDefinitions>
16         <Label>Test</Label>
17         <TextBlock Grid.Column="1">Test</TextBlock>
18         <TextBlock Grid.Row="1" Grid.Column="0">Test</TextBlock>
19         <TextBlock Grid.Row="1" Grid.Column="1">Test</TextBlock>
20         <TextBlock Grid.Row="2" Grid.Column="0">Test</TextBlock>
21         <TextBlock Grid.Row="2" Grid.Column="1">Test</TextBlock>
22         <TextBlock Grid.Row="3" Grid.Column="0">Test</TextBlock>
23         <TextBlock Grid.Row="3" Grid.Column="1">Test</TextBlock>
24     </Grid>-->
25     <!--<StackPanel Orientation="Horizontal">
26         <Label>Test</Label>
27         <TextBlock>Test</TextBlock>
28         <TextBlock>Test</TextBlock>
29         <TextBlock>Test</TextBlock>
30         <TextBlock>Test</TextBlock>
31         <TextBlock>Test</TextBlock>
32         <TextBlock>Test</TextBlock>
33         <TextBlock>Test</TextBlock>
34     </StackPanel>-->
35     <Canvas>
36         <Label Canvas.Left="10" Canvas.Top="5">Test</Label>
37         <TextBlock Canvas.Right="100" Canvas.Top="15">Test</TextBlock>
38         <TextBlock Canvas.Right="90" Canvas.Top="25">Test</TextBlock>
39         <TextBlock Canvas.Right="80" Canvas.Top="35">Test</TextBlock>
40         <TextBlock Canvas.Right="70" Canvas.Top="45">Test</TextBlock>
41         <TextBlock Canvas.Right="60" Canvas.Top="55">Test</TextBlock>
42         <TextBlock Canvas.Right="50" Canvas.Top="65">Test</TextBlock>
43         <TextBlock Canvas.Right="40" Canvas.Top="75">Test</TextBlock>
44     </Canvas>
45 </Window>

截圖,如圖4,5,6:

                    圖4 Grid

                    圖5 Canvas

                   圖6 StackPanel

 

 

結論:

  布局過程的復雜性直接取決於使用的 Panel 派生元素的布局行為。 例如,Grid 或 StackPanel 控件提供的功能比 Canvas 控件多很多。 功能大大提高的代價是性能成本也大大提高。 但是,如果不需要 Grid 控件提供的功能,則應使用成本較低的布局控件,如 Canvas 或自定義面板。

 

參考資料:

http://msdn.microsoft.com/zh-cn/library/bb613542.aspx

http://msdn.microsoft.com/zh-cn/library/ms745058.aspx#LayoutSystem_Measure_Arrange

 

前面查了下,附上Framework源碼下載地址:

http://referencesource.microsoft.com/netframework.aspx

 

如果大家覺得不錯請幫我點下推薦,謝謝~

轉載時,請注明本文來源:www.cnblogs.com/tmywu

作者:Tommywu

郵箱:tommywu23@126.com

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM