4

I have a WPF datagrid, bound to a list populated by linq-to-sql from a database. The binding is two-Way, allowing the user to change the values in each row

        <wpf:DataGrid AutoGenerateColumns="False" 
              ItemsSource="{Binding MyList}" 
              SelectedItem="{Binding SelectedItem}" >

When displaying about 20000 rows, the program crashes with an out-of-memory exception during initialisation of the list. Performance becomes unbearably slow even with less rows.

I know that on initialization the datagrid iterates through each row to measure the maximum column width and other properties. It will apparently do so for all rows regardeless of whether they are on screen.

I tried either binding the datagrid to myQuery.ToList() (to allow sorting the datagrid by clicking on the columns) or binding directly to the IQueryable. (Sorting does not work with that)

Both produce the same result. The ToList() with 20000 items alone does not cause the massive memory consumption, this happens only when it is bound to the datagrid .

Ignoring the issue of how useful 20000 rows in a datagrid are (those are the current requirements; to change those a working example would be helpful).

What is the most simple way to lazily load only the data currently shown on the screen, and ignore everything else until it is scrolled into view?

Can this be done without third party libraries and major code changes?

If not, what would be the recommended workaround?

HugoRune
  • 13,157
  • 7
  • 69
  • 144
  • Do you need DataGrid? DataGrid is a hog. ListView Gridview take a fraction of the resources as DataGrid. Are you giving the DataGrid a * height because if so it will not virtualize. Make sure you are getting a vertical scrollbar and go GridView. – paparazzo May 02 '12 at 22:47
  • @Blam Not sure if I need DataGrid, but according to [this answer](http://stackoverflow.com/a/4766730/145999) and [this answer](http://stackoverflow.com/a/1006507/145999) Datagrid would be the default choice since I want editing and sorting. What sort of height do I need to set? I am getting a scrollbar alright. – HugoRune May 02 '12 at 23:17
  • With GridView you need to implement the sort but it is not hard. GridView does not support edit. What I do is have an edit detail to the right where they can edit the selected row. Users like speed (and not locking up). – paparazzo May 02 '12 at 23:26
  • 1
    @Blam unfortunately MS Excel allows editing in-place and has no problems with large amounts of data, so users can just point at Excel and say it should work like that – HugoRune May 03 '12 at 17:11

2 Answers2

5

It turns out that the problem was entirely user error on my part:

WPF Datagrid can do UI virtualisation just fine: The memory consuming row objects are only drawn when required; if a row is outside the visible bounds of the datagrid, it will not be instantiated.

This will however not work if the datagrid is contained inside a ScrollViewer. Inside a scrollviewer every part of the datagrid is virtually visible, so the entire datagrid will be rendered. The scrollviewer then shows only the part of this rendered datagrid that fits inside the UI window.

Since a datagrid inside a scrollviewer looks just like a datagrid that manages its own scrollbars, I did not notice the scrollviewer.

With the scrollviewer removed, large amounts of rows cause no problem at all, even with variable height and width. The datagrid simply fills the available space, and only instantiates new rows as required.

So in short, the solution for my problem was: Do not put a datagrid inside a scrollviewer

HugoRune
  • 13,157
  • 7
  • 69
  • 144
0

The property that you are binding in this his case let's say that your MyList is made up of MyFile objects (List<MyFile>) then you have to create your MyFile class as:

 class MyFile
    {
        public string FullPath { get; set; }

        public string Extension
        {
            get
            {
                return Path.GetExtension(FullPath);
            }
        }

        public string PathRoot
        {
            get
            {
                return Path.GetPathRoot(FullPath);
            }
        }

        public DateTime CreationTime
        {
            get
            {
                return File.GetCreationTime(FullPath);
            }
        }

    }

That way you will store less info in each object and jut call the get method on the few items that are displayed in the grid. Versus having the actual values stored in the class. Hope this helps

Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • As I said, the list itself fits into memory without problems. The memory consumption happens only when binding it to the datagrid. As far as I can tell because datagrid will iterate through every row when it is initialised and retrieve and store rendering info. So storing less in each object will not change anything – HugoRune May 03 '12 at 06:47
  • You are not storing less with the technique that I showed. You will load the things that you need as they get displayed in the grid. This technique should significaly improve the performance on your grid. – Tono Nam May 04 '12 at 02:29