Sunday, February 13, 2011

ScrollViewer causing Slow Performance in WPF

In this post I am going to talk about the performance hit you might get in your WPF application if you are using ScrollViewer. ScrollViewer is used in WPF to display content in a smaller area as compared to content's actual size. In other words, if your content is large and you want to display it in a smaller area, you can put your content inside a ScrollViewer. ScrollViewer will then use scroll bars to display your complete content in smaller area.

If you are planning to use ScrollViewer or you are already using it in your application you might face some performance hit. This performance hit will only be visible if the content you are placing inside ScrollViewer is too large. You can download a ready to run application from here to see the performance hit with and without ScrollViewer. In my case, I am using ScrollViewer around a DataGrid whose ItemsSource is a DataTable. My DataTable contains 1000 rows. 1000 rows are more than enough to make sure that the content is large and it can't be displayed completely without scroll bars.
In my sample application's main view I have a Grid which contain two rows. First row has a Button while second row has a DataGrid enclosed inside a ScrollViewer.

   1:  <Grid>
   2:       <Grid.RowDefinitions>
   3:            <RowDefinition Height="30"/>
   4:            <RowDefinition/>
   5:       </Grid.RowDefinitions>
   6:       <Button Grid.Row="0" Click="Button_Click">Populate Grid</Button>
   7:       <ScrollViewer Grid.Row="1">
   8:            <DataGrid Grid.Row="1" Name="dataGrid" HorizontalAlignment="Stretch"  VerticalAlignment="Stretch"/>
   9:       </ScrollViewer>
  10:  </Grid>
As you can see in XAML above, I am handling click event of button. In this event I am creating a DataTable with 1000 rows and setting this DataTable as ItemsSource of my DataGrid.
If you run the application I specified earlier, you will see a "Populate Grid" button on top. Click this button to populate the DataGrid. By default, as you can see in XAML, DataGrid is inside ScrollViewer. When you will click "PopulateGrid" button, WPF will take around 5 seconds(at least on my system) to display the content of DataGrid. Now let's do a slight change in XAML and get rid of ScrollViewer.

   1:  <Grid>
   2:       <Grid.RowDefinitions>
   3:            <RowDefinition Height="30"/>
   4:            <RowDefinition/>
   5:       </Grid.RowDefinitions>
   6:       <Button Grid.Row="0" Click="Button_Click">Populate Grid</Button>
   7:       <!--<ScrollViewer Grid.Row="1">-->
   8:            <DataGrid Grid.Row="1" Name="dataGrid" MinRowHeight="25" AlternatingRowBackground="LightGray" MinColumnWidth="100" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ColumnHeaderHeight="35"/>
   9:       <!--</ScrollViewer>-->
  10:  </Grid>
As you can see I just have commented out the ScrollViewer. Now DataGrid is directly inside second row of Grid. After making these changes run the application again and click the populate button. This time WPF will display content of DataGrid without any delay or taking anytime.

So what could be the reason behind this performance hit? I think when we are placing the DataGrid inside ScrollViewer, all default UI Virtualizations of DataGrid are getting void as it is now inside ScrollViewer and as a result UI is taking so much time to load the contents of DataGrid.
You can download the ready to run application from here and play with both scenarios.

1 comment:

  1. Off course the UI virtualization is being turned off!

    ScrollViewer passes its child on the measure a height which is double.PositiveInfinity.

    In order for virtualization to work, the content has to implement IScrollInfo, which in that case, the scrollviewer can tell the child the size of the viewport, the offset, get the size of the extent etc...

    It is also logical that the datagrid already contains a scrollviewer around it's panel.

    ReplyDelete