24
votes

I have a performance problem with the (WPF Toolkit) DataGrid. It contains about 1.000 rows (only eight columns) and scrolling is horribly slow and laggy. Also the initial load of the Window containing the DataGrid takes 5-10 seconds.

I did some research (using google and StackOverflow) but couldn't find anything besides the advice to turn on UI virtualization. But even after explictly enabling that scrolling continues to be awfully slow.

My DataGrid is bound to an ICollectionView / CollectionViewSource. It's is defined in XAML like this (the columns are explicitly defined, not auto generated):

    <tk:DataGrid x:Name="dataGrid" 
                 ItemsSource="{Binding Path=Bookings}" 
                 AutoGenerateColumns="False" 
                 Grid.Row="1" 
                 EnableRowVirtualization="True" 
                 EnableColumnVirtualization="True"
                 VirtualizingStackPanel.IsVirtualizing="True"
                 VirtualizingStackPanel.VirtualizationMode="Recycling">
            ... 
    </tk:DataGrid>

The DataContext for the entire Window is set to an instance of the class containing the ICollectionView the DataGrid is bound to.

Every blog or forum post I found was praising the DataGrid's performance so I'm quite obviously doing something seriously wrong. Since I'm quite new to WPF in general and especially to the DataGrid I've no clue of how to improve this. Does anybody have some advice for me? What's your experience with the DataGrid? What am I doing wrong?

Edit: Just followed this question's advice to set the Width of all columns to "Auto". That did not change the bad scrolling performance. Also I'm not using DataGridTemplateColumns (just some DataGridTextColumns and two DataGridComboBoxColumns).

Edit2: I used Snoop to look at my app. What I see suggests that virtualization is indeed working (only 19 rows, not a thousand). But every row contains 52 elements, so those add up to more than thousand elements. Might that be a / the problem?

Thanks a lot!

7
I updated my answer with a quick way to check if virtualization is working - it would be nice if you could rule out that this is not the problem.Egor

7 Answers

19
votes

The DataGrid has an Attached property, ScrollViewer.CanContentScroll, that manages this behavior. To get smooth scrolling you'll need to set it to False.

6
votes

After finally making the time to build my application against an up-to-date version of WPF the scrolling problem seems completely gone. So if anyone still uses the toolkit version of the DataGrid just "update" to the version included in the framework and you should be fine.

4
votes

I am using .NET 4.0 and still get the scroll performance problem. What I did is - disabled virtualization. I set EnableRowVirtualization to 'false' in the DataGrid. This considerably improved the scroll performance.

I would suggest to not assume that whatever is being offered by WPF is useful in all the situations.

3
votes

What container does your datagrid live in? For example - if you put it in a scrollviewer, the datagrid will grow to display every row, thus effectively disabling virtualization (and the scrollviewer will make it appear normal while this happens). Make sure that the datagrid size is bounded.

It really does sound like a virtalization thing, if this advice doesn't work run your app through a profiler to make sure virtualization is happening.

Edit: Here is an example of how to use snoop (or mole I guess) to quickly see if the virtualization is working. http://blogs.msdn.com/jgoldb/archive/2008/03/25/quick-tips-to-improve-wpf-app-memory-footprint.aspx

2
votes

You could try adding the items one by one (or row by row) in the datagrid and updating the UI thread after each addition. That way, the user sees the loading take place and it doesn't seem like the application is doing nothing. See here a more detailed description of this method

2
votes

As far as initial loading goes, I found it neccessary to extend the public API to dramatically improve large # of column loading - we're talking minutes to under a second. That said, I have similar issues with scrolling performance, even 500+ columns is really slow to scroll.

Setting columns within my derived datagrid:

var columns = new DataGridColumnCollection(true, dataGrid);
        for (int i = 0; i < pivotTable.DetailsColumnCount; i++)
        {
            if (!pivotTable.NullColumns.Contains(i))
            {
                columns.Add(new PivotDetailColumn(pivotTable, i));
            }
        }
        columns.ForceUpdate();

        dataGrid.Columns = columns;

        dataGrid.ItemsSource =
            Enumerable.Range(0, pivotTable.DetailsRowCount)
                .Where(i => !pivotTable.NullRows.Contains(i)) // Only non null rows
                .ToList();

Fixes to DataGridColumnCollection:

public class DataGridColumnCollection : ObservableCollection<DataGridColumn>
{
    private bool _DeferColumnChangeUpdates = false;

    public DataGridColumnCollection(bool deferColumnChangeUpdates, DataGrid dataGridOwner)
        : this(dataGridOwner)
    {
        _DeferColumnChangeUpdates = deferColumnChangeUpdates;
    }

    public DataGridColumnCollection(DataGrid dataGridOwner)
    {
        Debug.Assert(dataGridOwner != null, "We should have a valid DataGrid");

        DisplayIndexMap = new List<int>(5);
        _dataGridOwner = dataGridOwner;

        RealizedColumnsBlockListForNonVirtualizedRows = null;
        RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows = null;
        RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;

        RealizedColumnsBlockListForVirtualizedRows = null;
        RealizedColumnsDisplayIndexBlockListForVirtualizedRows = null;
        RebuildRealizedColumnsBlockListForVirtualizedRows = true;
    }

    #region Protected Overrides

    public void ForceUpdate()
    {
        if (DisplayIndexMapInitialized)
        {
            UpdateDisplayIndexForNewColumns(this, 0);
        }

        InvalidateHasVisibleStarColumns();
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
            if (!_DeferColumnChangeUpdates)
            {
                if (DisplayIndexMapInitialized)
                {
                    UpdateDisplayIndexForNewColumns(e.NewItems, e.NewStartingIndex);
                }

                InvalidateHasVisibleStarColumns();
            }
                break;
0
votes

I've found that having a column's width set to Auto (the default!) can introduce substantial vertical scroll lag when there are many cells. Switching to fixed-width helped a lot in my case, though there is still some detectable lag when there are many columns.

You can set CanUserResizeColumns="True" on the grid if you're worried users won't like the widths you select.