40

In my .NET 3.5 WPF Application, I have a WPF DataGrid which will be populated with 500 columns and 50 rows. The performance of App is very very poor in scrolling, or when I do DataGrid.Items.Refresh() or in selecting rows.

Actually App will take around 20 sec to Update Layout. Layout_Updated() event will trigger after 20 sec.

If I reduce the columns to 50 or less, App will be very responsive. As per my findings performance is directly related to column count.

How do I improve the DataGrid performance?

Kishor
  • 4,006
  • 7
  • 30
  • 47
  • 6
    A Grid with more that even 10 columns may not be a good idea. But consider `Custom Paging` – Boomer Dec 07 '12 at 14:06
  • 4
    @Boomer Really? EVER seen a forex grid? – TomTom Dec 07 '12 at 14:07
  • @WPFK Have you considered to use a third party grid that is optimized for performance? – TomTom Dec 07 '12 at 14:07
  • @TomTom - Can you tell some third party grid for me. – Kishor Dec 07 '12 at 14:14
  • No, but you COULD try Infragistics - I uses them in financial trading front ends and their performance is terrific. – TomTom Dec 07 '12 at 15:05
  • Do you need update (edit)? If not go with ListView GridView. Way faster. Also in my experience binding to a List is faster than binding to a DataTable (for sure the List takes less memory). – paparazzo Dec 07 '12 at 16:11
  • 3
    I don't know what forex is but I don't think any sane human being will ever scroll thru 500 columns of data. – Federico Berasategui Dec 07 '12 at 18:08
  • 500 columns is definately going to be rough on performance... but at least with intial loads and scrolling, make sure the DataGrid's attached property ScrollViewer.CanContentScroll = True (to ensure virtualization of the rows that are not being displayed). If you had set that value to False to enable "Smooth scrolling", then all rows will be rendered even when not shown. – Scott Dec 07 '12 at 18:25

7 Answers7

94

There are a few options you can turn on to help you on your DataGrid object

EnableColumnVirtualization = true
EnableRowVirtualization = true

These two are the main ones I think might help. Next try making your binding async

ItemsSource="{Binding MyStuff, IsAsync=True}"

And lastly, I've heard that setting a maximum height and width can help even if it above the max screen size, but I didn't notice a difference myself (claim had to do with auto size measuring)

MaxWidth="2560"
MaxHeight="1600"

Also never put a DataGrid in a ScrollViewer, because you will essentially lose virtualization. Let me know if this helps!

Jon
  • 9,156
  • 9
  • 56
  • 73
Alan
  • 7,875
  • 1
  • 28
  • 48
  • I don't think EnableColumnVirtualization and EnableRowVirtualization are available until .NET 4.0. So the OP would need to upgrade from 3.5 before looking into the first suggestions. – Scott Dec 07 '12 at 18:19
  • @Scott Well that could be a reasonable solution. To the OP, why are you using .NET 3.5? .NET 4.0 is available on XP SP3 and many 3rd party libraries already require .NET 4.0 if you want to use them. – Alan Dec 07 '12 at 18:58
  • 2
    I would also make your columns a fixed width. – Kelly Apr 15 '13 at 16:44
  • @Alan I'm curious as to how you go about having a large data grid that avoids being inside a ScrollViewer? – testpattern Jan 09 '14 at 14:48
  • 1
    @testpattern The datagrid scrolls on its own, you just don't want to put it in your own ScrollViewer external to the DataGrid otherwise you'll lose virtualization of controls. – Alan Jan 09 '14 at 14:51
  • I set my MaxHeight to 800 as a i had two scroll bars showing, maybe cause i had the grid wrapped in dockpanel but that fixed the (scrollbar) issue now i can load 1000+ items from XML into datagrid easily,fast and async without a slow render of the grid! thx – BENN1TH Mar 17 '15 at 11:45
  • 4
    Amazing. I wonder why other threads never talked about this Enable Row and Column Virtualization. Thanks a ton. – Vikas Jun 18 '15 at 19:20
  • I am suprised people adding ScrollViewer to DataGrid. Could that be previous version of wpf datagrid does not support scrolling? –  Jun 14 '17 at 03:49
  • 1
    Definitely worth giving a try! And in fact they improved the performance a lot for me. Thanks @Alan – Siva Gopal Feb 11 '19 at 10:22
  • 1
    Setting the MaxHeight made a significant improvement. Thank you very much. Also setting MaxWidth made a slight improvement as well. I was going crazy until I found this solution. Thank you. – Zeyad Dec 23 '19 at 06:08
  • 1
    It really helps! After remove that ScrollViewer, the rerender performace of Datagrid goes much better! – BangZ Jan 16 '23 at 09:59
11

Check if you have property ScrollViewer.CanContentScroll set False. Setting this property to false disables the virtualization in a way will degrade the performance of your Data-grid. For more clarification refer this CanContentScroll

Konrad Borowski
  • 11,584
  • 3
  • 57
  • 71
Rajat Ghalni
  • 111
  • 1
  • 2
3

Set the DataGrid.RowHeight value and that will make a huge difference.

I know this is a really old question, but I just came across it, and this was the biggest difference on my end. My default height was 25.

chris84948
  • 3,940
  • 3
  • 22
  • 25
1

Step 1: From 2 minutes to 10 seconds

This answer (Set ScrollViewer.CanContentScroll to True) put me on the right track. But I need it to be set to false. To set it to true when I'm doing my refresh I wrote this two methods.

internal static void DataGridRefreshItems(DataGrid dataGridToRefresh)
{
    /// Get the scrollViewer from the datagrid
    ScrollViewer scrollViewer = WpfToolsGeneral.FindVisualChildren<ScrollViewer>(dataGridToRefresh).ElementAt(0);
    bool savedContentScrollState = scrollViewer.CanContentScroll;
    scrollViewer.CanContentScroll = true;

    dataGridToRefresh.Items.Refresh();

    /// Was set to false, restore it
    if (!savedContentScrollState)
    {
        /// This method finishes even when the update of the DataGrid is not 
        /// finished. Therefore we use this call to perform the restore of
        /// the setting after the UI work has finished.
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => SetScrollViewerCanContentScrollFalse(scrollViewer)), DispatcherPriority.ContextIdle, null);
    }
}

private static void SetScrollViewerCanContentScrollFalse(ScrollViewer scrollViewer)
{
    scrollViewer.CanContentScroll = false;
}

This is the method I use to get the VisualChildren:

public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj != null)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
            if (child != null && child is T)
            {
                yield return (T)child;
            }

            foreach (T childOfChild in FindVisualChildren<T>(child))
            {
                yield return childOfChild;
            }
        }
    }
}

After this my refresh of 50.000 new items lasts only 10 seconds unlike 2 Minutes and consumes only 2 MB of RAM instad of 4 GB before.

Step 2: From 10 seconds to 0.5 seconds

For testing I disabled all of my IValueConverter and implemented properties which I bind directly. Without the converters the DataGrid refreshes immediately. So I left it.

marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52
0

Maybe try this instead of loading all 50 rows at once

http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization

Constanta
  • 399
  • 4
  • 15
  • Thanks for suggestions. But my requirement to have 500 columns at once. – Kishor Dec 07 '12 at 14:13
  • 1
    well you said scrolling so I thought you don't display all rows at once (how do you do that anyway with 500 cols) The idea with data virtualization is that you load a certain number to populate your grid and then remove and replace rows as you scroll – Constanta Dec 07 '12 at 14:23
0

One soltuion that I found to be the best compromise for both loading and scrolling performance is to set the IsAsync=True for all the columns' Binding not the DataGrid ItemsSource Binding. Example:

<DataGridTextColumn Binding="{Binding Path=MaterialName, Mode=OneTime, IsAsync=True}" Header="Name" />

By the way, this solution works better when virtualization on the DataGrid is switched off. However, even if virtualization is switched on, still worth a try. Also this solution is very effective when applied to a DataGridTemplateColumn that displays an image or so.

Zeyad
  • 537
  • 2
  • 7
  • 15
0

I was having a DataGrid in a ScrollViewer allowing for the DataGrid to grow to infinite height. Setting the Height or the MaxHeight of the DataGrid to something reasonable was solving the majority of my performance problems.

Oskar Sjöberg
  • 2,728
  • 27
  • 31