10

I've always had long loading times with WPF DataGrids, and I cannot find any similar reports online, so I suspected that I was doing something wrong. Now I am sure of it, since adding layout complexity considerably slows down execution. In a very simple layout, the DataGrid populates instantaenously, whereas the code below takes around 3 seconds to execute.

In the following code, it takes ~3 seconds for 150 rows and 11 columns to load, even if each cell is not bound to any property and with AutoGenerateColumns=False. (I have a two core, 2.6GHz processor with plenty of RAM).

The bottle neck takes place when the ItemsSource property is set in a layout as the one below:

<Window x:Class="datagridtest.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">
<Border  Background="LightSteelBlue" CornerRadius="10" Margin="10">
    <ScrollViewer Margin="10" HorizontalScrollBarVisibility="Auto">
        <Grid Margin="10,50,0,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"  />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />

            </Grid.RowDefinitions>
            <Expander IsExpanded="True" Name="expander1"  Grid.Row="0">
                <Grid>
                    <DataGrid VirtualizingStackPanel.IsVirtualizing="True" AutoGenerateColumns="false" Name="dg" Height="auto" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False">
                        <DataGrid.Columns>
                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>



                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>


                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>

                            <DataGridTextColumn >
                                <DataGridTextColumn.Header >
                                    <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>



                        </DataGrid.Columns>
                        </DataGrid>
                </Grid>
            </Expander>

            <Expander IsExpanded="true"  Grid.Row="1">
                <Grid>
                    <DataGrid AutoGenerateColumns="True"  Height="auto" />
                </Grid>
            </Expander>

            <Expander IsExpanded="true"    Grid.Row="2">
                <Grid>
                    <DataGrid AutoGenerateColumns="True" Height="auto" />
                </Grid>
            </Expander>
            <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="121,-42,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click_2" />
        </Grid>
    </ScrollViewer>
</Border>

using System.Collections.ObjectModel;

namespace datagridtest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();


    }

    class row
    {
        public string Name { get; set; }
        public double Age { get; set; }
    }



    private void button1_Click_2(object sender, RoutedEventArgs e)
    {
        ObservableCollection<row> src = new ObservableCollection<row>();

        for (int i = 0; i < 150; i++)
            src.Add(new row { Name = i.ToString(), Age = i / 2 });

        dg.ItemsSource = src;
    }
}
}
Eugenio De Hoyos
  • 1,755
  • 16
  • 22

4 Answers4

17

The problem only occurs when the DataGrid is embedded inside a ScrollViewer like:

<ScrollViewer>
    <Datagrid/>
</ScrollViewer>

This makes sense because this configuration causes the whole DataGrid to be drawn at the same time (to be able to size the ScrollViewer's client area correctly). In essence, it overrides the built-in virtualization behavior of the DataGrid, which implements its own ScrollBars so that not all of its content has to be placed in the layout simultaneously.

In other words, embedding a DataGrid inside a ScrollViewer is rarely needed because the DataGrid has its own automatic scrolling.

Eugenio De Hoyos
  • 1,755
  • 16
  • 22
  • 3
    Furthermore, don't ever let a DataGrid autosize in any dimension. If you are planning to stack them inside other controls, it's a good idea to set its MaxHeight property to something smaller than your screen's size, since DataGrid does not seem to be very efficient when its layout is large. – Eugenio De Hoyos Jul 26 '10 at 18:41
  • +1 Lifesaver! I temporarily put in a ScrollViewer and forgot all about it.. was shocked when I saw the grid crawl on an update. – Gishu Dec 11 '12 at 06:28
7

I had a similar problem with a UserControl that contained a DataGrid, sometimes when I placed the UserControl on a new form or another UserControl it would lock up the interface (5 seconds?) while it redrew the DataGrid. Same with Resizing.

I tracked it down to

RowDefinition Height="Auto"

and the same performance issue also happened if I put the UserControl in a StackPanel. Seems very much to do with the previously mentioned resizing bug when the whole datagrid needs to be popluated to calculate the size of the encapsulating container.

<UserControl x:Class="ExampleUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" d:DesignHeight="481" d:DesignWidth="773">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" /> <!-- 'AUTO' CAUSES EXTREMELY POOR PERFORMANCE -->
        </Grid.RowDefinitions>

        <Grid Grid.Row="0"> <!-- CHANGING TO STACKPANEL CAUSES EXTREMELY POOR PERFORMANCE -->
            <ContentControl Content="{Binding MyDataGridUserControl}" />
        </Grid>
    </Grid>

</UserControl>

I just discovered that setting MaxHeight="[whatever]" for the ContentControl also works, as per a previous comment. It can be larger than the screen.

TripleAntigen
  • 2,221
  • 1
  • 31
  • 44
  • 1
    Thanks for pointing out the fact that when Height is set to "Auto" the problem arises. In my answer I didn't explicitly emphasize that. I wouldn't, however, say that this is a bug, just a design issue. When a data grid handles its own scrollbars and "virtual" client area, many optimizations are possible, but we cannot expect all these optimizations to be in effect when the virtual area is handled by the parent control--or can we? Anyways, at present, the data grid seems to be designed for a very specific use :( It sucks because it causes us to have scrollbars within scrollbars in some UIs. – Eugenio De Hoyos Sep 14 '11 at 13:14
  • This sort of brings a limitation to WPF that we wouldn't expect there to be. – Eugenio De Hoyos Sep 14 '11 at 13:16
  • 1
    Thank you for this answer - it was indeed the issue I was having. After removing the Height="Auto" for data grid row I had a huge performance increase. – Don Gossett Jun 24 '16 at 18:14
3

Can you see if all rows are being generated layout-wise? Normally Virtualization should hinder that and only generate the visible rows. (Test it with a template in one of the columns and count it in the constructor). There is a bug if WPF cannot determine the correct width of the DataGrid as it tries to size to the largest column - thus having to generate all rows to calculate the one with the largest width. (To test the last one - place it in a dock-panel instead of a grid - docked left or right)

Also, try VirtualizingStackPanel.VirtualizationMode="Recycling" to let it recycle the templates used.

Goblin
  • 7,970
  • 3
  • 36
  • 40
  • Thanks Goblin! You were on the right track in that my DataGrid was trying to generate all of the rows at the same time. But this was due to embedding the DataGrid inside a ScrollView. Virtualization was not the issue (because the size of the DataGrid was not being constrained to screen dimensions). – Eugenio De Hoyos Jul 26 '10 at 18:29
0

I have the same problem with bound Data grid, and I notice that in first load it is fast but on second and next it is slow. So when I add in code:

DataGrid.ItemsSource = Nothing

and then

TableAdapter.Fill(Mydataset.MyStoredProcedure,....)
DataGrid.ItemsSource=Mydataset.MyStoredProcedure

it became very FAST.

miselking
  • 3,043
  • 4
  • 28
  • 39
SLAVICA
  • 9
  • 1