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

 

 

 

 

 

posted @ 2013-05-13 13:33  老鱼_678  阅读(2896)  评论(6编辑  收藏  举报