WPF ListView Performance – Booooost für Templates

by robert 9. June 2009 18:08

Folgendes Video zeigt eine WPF ListView mit 2Millionen einträgen:

 

Out Of the Box ist der WPF ListView rasant schnell. Verwendet man jedoch ein Semi-kompliziertes DataTemplate, bricht die Performance ein.

Der Problem-Listview

im Gegensatz zum Beispielvideo benötigt der folgende ListView schon bei 1490 Elementen eine Renderzeit von fast 10 Sekunden.

image

Hier die Definition des ListViews:

<ListView 
  Grid.Row="1"
  x:Name="lvLogItems"
  ItemsSource="{Binding Source={StaticResource myList}}"
  ScrollViewer.HorizontalScrollBarVisibility="Disabled"
  ScrollViewer.VerticalScrollBarVisibility="Visible"
  HorizontalContentAlignment="Stretch"
  HorizontalAlignment="Stretch"
  Style="{StaticResource lbResultStyle}"
  ItemContainerStyle="{StaticResource lbResultItemStyle}"        
  ItemTemplate="{StaticResource itemTemplate}">
</ListView>
 

Die Definition des ItemTemplates:

<DataTemplate x:Key="itemTemplate">      
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="50" />
      <RowDefinition Height="1" />
    </Grid.RowDefinitions>
    <DockPanel Grid.Row="0">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="30" />
          <ColumnDefinition />
          <ColumnDefinition Width="130"/>
          <ColumnDefinition Width="22"/>
        </Grid.ColumnDefinitions>
        
        <DockPanel Grid.Column="0" Margin="0,1,0,0" > <!--Background="AliceBlue"-->
          <Image Name="imgStatus" Height="20" VerticalAlignment="Top"/>
        </DockPanel>
 
        <DockPanel Grid.Column="1" Margin="3,1,0,0"><!--Background="BurlyWood"--> 
          <TextBlock Text="{Binding Path=StatusText}" TextWrapping="Wrap"></TextBlock>              
        </DockPanel>
        
        <Grid Grid.Column="2">
          <Grid.RowDefinitions>
            <RowDefinition Height="16" />
            <RowDefinition Height="6" />
            <RowDefinition Height="12" />
            <RowDefinition Height="12" />
          </Grid.RowDefinitions>
          
          <DockPanel>
            <TextBlock Grid.Row="0" Text="Dauer: " FontSize="16" FontWeight="ExtraLight"  />
            <TextBlock Grid.Row="1" Text="{Binding Path=Duration}" FontSize="16" FontWeight="bold"></TextBlock>
          </DockPanel>
          
          <DockPanel Grid.Row="2">
            <TextBlock Grid.Row="0" Text="Von: " FontSize="11" Width="28" />
            <TextBlock  Text="{Binding Path=StartTime, StringFormat=\{0:MM:dd.yy HH:mm:ss\}}" FontSize="11"></TextBlock>
          </DockPanel>
          <DockPanel Grid.Row="3">
            <TextBlock Grid.Row="0" Text="Bis: " FontSize="11" Width="28" />
            <TextBlock  Text="{Binding Path=EndTime, StringFormat=\{0:MM:dd.yy HH:mm:ss\}}" FontSize="11"></TextBlock>
          </DockPanel>
          
        </Grid>
        
        <Image Grid.Column="3" Source="..\Resources\application_view_detail.png" 
          Margin="0,2,3,0"
          Height="16" VerticalAlignment="Top"></Image>
 
        <Image Grid.Column="3" Source="..\Resources\email.png" 
          Margin="0,20,3,0"
          Height="16" VerticalAlignment="Top"></Image>
 
      </Grid>
 

Hier die Defintion des ItemContainerStyles:

<Style x:Key="lbResultStyle" TargetType="ListView">
  <Setter Property="Margin" Value="0"/>
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate >
        <StackPanel IsItemsHost="True" 
            Orientation="Vertical"
            Width="{Binding ElementName=lvResult,Path=ActualWidth}" >
        </StackPanel>
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
</Style>

 

VirtualizingStackPanel zur Hilfe

Die Reduzierung der zähen Ladezeit erfolgt über eine triviale Änderung: Statt der Verwendung des StackPanel als ItemHost wird ein “VirtualizingStackPanel“ verwendet:

<Style x:Key="lbResultStyle" TargetType="ListView">
  <Setter Property="Margin" Value="0"/>
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate >
        <VirtualizingStackPanel IsItemsHost="True" 
            Orientation="Vertical"
            Width="{Binding ElementName=lvResult,Path=ActualWidth}" >
        </VirtualizingStackPanel>
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
</Style>

Der Effekt ist eine dramatische Verkürzung der 10-sekündígen Renderzeit hinzu einer sofortige Darstellung des Grids.

Wie funktioniert es?

Das Zauberwort in WPF heißt Virtualisierung: Der gewaltige Performance Unterschied wird erreicht, durch die Art und Weise wie das VirtualizingStackPanel die Scrollhöhe bestimmt. Statt die Höhe eines jeden Elements auszurechnen (und dafür zu rendern), wird nur der Index des Elements für die Bestimmung der ScrollPosition verwendet. Wird nun gescrollt müssen nur die Elemente gerendert werden, die tatsächlich sichtbar sind.

Lesenswert:

enjoyed the post?

Tags:

WPF

Comments are closed

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC 4 and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.