2011年5月20日 星期五

DataGrid 在 StackPanel 下的效能問題

一個無意間,觸碰到了地雷。而這個地雷,是有原因的。

WPF 的 xaml 中,如果在 StackPanel 中含有 DataGrid,是常見的事。但是,如果 DataGrid bindind 到大量的資料時,就有些問題了。

程式

下面是我的 xaml

xaml

<Window x:Class="LargeDataInDataGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
  <StackPanel>
    <Label Content="資料筆數" />
    <TextBox Name="txtRecNo" TextChanged="txtRecNo_TextChanged" Text="100" />
    <DataGrid Name="dgResult" ItemsSource="{Binding}"/>
  </StackPanel>
</Window>

 

程式運作如下圖

SNAGHTML575c70

現象

當資料只有100筆時,看起來很正常。但看一下工作管理員,天啊,要秏掉記憶體 72 MB。WPF 這麼秏 memory?

image

如果將筆數調整到 1000筆呢?剛改完數字,WPF 程式彷彿當掉一樣。hang 住個幾秒鐘後,看一下記憶體,增加到 187 MB了。

SNAGHTML5b7c97

image

原因

原來, StackPanel 的用途,是儘量地長出 Child control 所要使用的空間 (space)。DataGrid 要長出 100 筆資料的空間,就計算並配置所要的空間及記憶體。所以當 DataGrid 要長出1000筆時,記憶體的使用量就不得了。

但是,我們看不到這麼多筆啊?雖然看不到, StackPanel 仍然堅持配置記憶體呢!

我們將 StackPanel 換成 Grid來試試看。

<Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="30"/>
      <RowDefinition Height="30"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Label Content="資料筆數"  />
    <TextBox Name="txtRecNo" Grid.Row="1" TextChanged="txtRecNo_TextChanged" Text="100" />
    <DataGrid Name="dgResult" ItemsSource="{Binding}" Grid.Row="2"/>
  </Grid>

 

換成 Grid 後,即使是1000筆,記憶體只用了 71 MB,明顯地少掉許多,程式運作時也不會有問題。

image

為什麼 Grid 可以運作正常呢?原來 Grid 運作時是有計算Row 的高度的。DataGrid 在顯示時 Row 高度明顯有限制,因此要求 DataGrid 有高度的限制。DataGrid 的 Height 預設是 Auto,不足時 ScrollBar 就會出現。此時 UI Virtulization 的機制開始運作,只需要顯示看的到的區域即可 ,因此記憶體使用量變少。

如果我還是需要在 StackPanel 使用 DataGrid 呢?只需要設定 DataGrid 的 Height 或 MaxHeight 就可以了。

<StackPanel>
      <Label Content="資料筆數" />
      <TextBox Name="txtRecNo" TextChanged="txtRecNo_TextChanged" Text="100" />
      <DataGrid Name="dgResult" ItemsSource="{Binding}" MaxHeight="500"/>
    </StackPanel>

沒有留言:

Share with Facebook