none
DataGrid loses virtualization when sorting by one column

    Question

  • I am using a WPF DataGrid that is bound to a custom ItemsSource that implements IList (and NOT IEnumerable). The custom ItemsSource performs data virtualization and only loads pages of items as needed. MyDataGrid inherits from DataGrid and overrides the Sorting method so that I can maintain data virtualization while sorting. I have UI Virtualization turned on for MyDataGrid.

    When I run the application, MyDataGrid displays just fine, tells me I have roughly 20,000 items, and only asks my ItemsSource for the first 20 or so items. I then can click on column headers to sort various columns and again my ItemsSource only has to reload the first page of 40 items because MyDataGrid has asked to refresh only the first 20 or so.

    BUT, when I click on my Last Name column to sort, MyDataGrid loses its UI Virtualization and iterates through every single item by index in the ItemsSource even though it only needs the first 20 or so (which of course causes my IList to ask my database for a page of 40 items approximately 450 times).  Why does it punish me so!!!???

    One hint may be that, prior to sorting by Last Name, the first item in MyDataGrid has a last name that starts with the letter V (very late in the alphabet), and that I can actually sort the list in Descending order by last name without losing virtualization. The same thing happens with my Title column which has a first item that starts with the letter S--again I can sort the Title column in descending order without losing virtualization, but sorting in ascending order causes every item in the IList to be itereated). I don't have the problem with any of the other columns.  The only thing that changes when I click on a column to sort it is a string parameter that is sent to the database stored procedure--so there is nothing I do differently in code for those 2 columns.

    I am on .Net version 4.6.1.  Is this a recognized problem?  Any ways around it?  Any ideas???

    Monday, January 18, 2016 4:19 PM

Answers

  • FINALLY FIGURED IT OUT!!!  Looking through the call stack, it appeared that the problem was happening during the MeasureOverride of the VirtualizingStackPanel and I noticed it was calling the SyncUniformSizeFlags method.  So I then went into the default style for the DataGrid and set the RowHeight to a fixed amount and it no longer causes the problem.

    So, it appears that the DataGrid can lose its virtualization when a fixed RowHeight is not set.  Still don't know why opting to sort in ascending order by Last Name or Title caused it to want to resync the row heights, but I can work around that one.

    • Marked as answer by JeffBurdek Monday, January 18, 2016 9:02 PM
    Monday, January 18, 2016 9:02 PM

All replies

  • If you use observablecollection<t> instead of a custom whatever it is you wrote then does it display the same issue?

    I would guess not.

    Observablecollection enables sorting when you click column headers, straight out the box.

    List does not.

    So I don't know what you have there but I'd want a very good reason for using anything other than an observablecollection or bindinglist.

    .

    Also.

    Presenting 20,000 of anything to a control is not a good plan.

    You should allow the user to filter or somehow select a subset.

    If you have more than 4 screens worth of data then that is longer than you should be using.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Monday, January 18, 2016 4:36 PM
    Moderator
  • >>I am using a WPF DataGrid that is bound to a custom ItemsSource that implements IList (and NOT IEnumerable).

    IList dervies from IEnumerable and an ItemsSource collection must implement the IEnumerable interface. Otherwise you cannot set the ItemsSource property to it. The type of the ItemsSource of the DataGrid is IEnumerable.

    >>Is this a recognized problem?  Any ways around it?  Any ideas???

    The DataGrid control doesn't sort the source collection, it sorts the ICollectionView that sits in between the source collection and the DataGrid. You are actually binding to this CollectionView and not to the source collection itself.

    When the source collection implements the IList interface, a ListCollectionView is created and this one has a CustomSort property that you could set to a custom implementation of the IComparer to implement custom sorting. Plrase refer to the the following links for more information about this:
    https://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/
    https://ligao101.wordpress.com/2007/07/31/a-much-faster-sorting-for-listview-in-wpf/

    Hope that helps.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Monday, January 18, 2016 5:46 PM
  • I don't use ObservableCollection because it does not support data virtualization and I am not going to load 20,000 records from the database when only 20 or so are needed to display on the screen.  And the problem is not with the IList implementation, the problem is that the DataGrid is asking for EVERY ONE OF THE 20,000 ITEMS in the IList even when UI Virtualization is turned on.

    Just to be completely clear, as said earlier, my IList performs DATA VIRTUALIZATION which means it reports a total count of 20,000 items and only loads those items for which the view asks.  The DataGrid is set for UI VIRTUALIZATION which means it only looks at the items it needs to populate the visible portion of the DataGrid.  The first time the grid is loaded up, it only makes one call to the database which returns the first page of 40 records because the UI has asked for only the 20 or so items that are visible on screen.  I click on column headers to sort and everything works just fine--the DataGrid asks my IList for only the first 20 or so items to display on the screen.  BUT when I sort in ascending order on the LastName or Title column headers, the UI Virtualization is somehow turned off or ignored and the DataGrid asks for every one of the 20,000 items in the list as if it was rendering in a StackPanel and had unlimited space and was going to render every item.  This problem does NOT occur when I choose to sort those same 2 columns in DESCENDING order though.
    Monday, January 18, 2016 6:04 PM
  • Oh, my IList DOES NOT implement IEnumerable (which would cause the DataGrid to ask for every one of the 20,000 items)
    Monday, January 18, 2016 6:10 PM
  • Oh, my IList DOES NOT implement IEnumerable (which would cause the DataGrid to ask for every one of the 20,000 items)

    It certainly does: https://msdn.microsoft.com/en-us/library/system.collections.ilist%28v=vs.110%29.aspx. All ItemsSource collections must implement the IEnumerable interface.

    Please read my previous reply for more information.


    Please remember to close your threads by marking all helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.


    Monday, January 18, 2016 6:12 PM
  • Correct you are!
    Monday, January 18, 2016 6:48 PM
  • Yes, I looked at that article previously, but if I'm reading it correctly I would have to load up all 20,000 items in order for the IComparer to do its job.

    You are correct when you say that IList does in fact implement IEnumerable (through ICollection)--I just learned something new :-)   --but I know that when my custom list DIRECTLY implemented IEnumerable, items controls would not virtualize (per http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization). So I removed IEnumerable from its signature and was able to virtualize again.

    Maybe something inside the DataGrid or data itself is causing the ICollectionView to revert back to the inherited IEnumerable implementation and ask for every item when I choose to sort by LastName in ascending order.......???  If I only knew what was triggering this behavior I might be able to code around it.  Implement my own ICollectionView?  Doesn't seem like I should have to.
    Monday, January 18, 2016 7:03 PM
  • When you sort.

    How do you think it can sort the collection without having all of it?

    How is it supposed to know where records go if it doesn't have them?

    You can virtualise your data only if you know what it's going to show.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Monday, January 18, 2016 7:44 PM
    Moderator
  • Andy, Data Virtualization is different than UI Virtualization. The article I mentioned above is a good start (http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization). The database performs the sorting, not the ListCollectionView nor the DataGrid. This is pretty much everything that happens in my override of Sorting in the DataGrid: e.Handled = true; itemsSource.SortDescriptions.Add(new SortDescription(e.Column, e.Column.SortDirection.Value)); Marking the event as Handled prevents the ListCollectionView (the UI) from doing the sorting. Adding a SortDescription to the itemsSource results in a reload of the first page of 40 records from a SQL Server Stored Procedure and raises the Reset flag on the ItemsSource so that the DataGrid knows to re-render. The problem here is not the sorting, the problem here is that UI Virtualization is enabled and yet in select instances the DataGrid/ListCollectionView is behaving as if it is not.
    Monday, January 18, 2016 8:42 PM
  • FINALLY FIGURED IT OUT!!!  Looking through the call stack, it appeared that the problem was happening during the MeasureOverride of the VirtualizingStackPanel and I noticed it was calling the SyncUniformSizeFlags method.  So I then went into the default style for the DataGrid and set the RowHeight to a fixed amount and it no longer causes the problem.

    So, it appears that the DataGrid can lose its virtualization when a fixed RowHeight is not set.  Still don't know why opting to sort in ascending order by Last Name or Title caused it to want to resync the row heights, but I can work around that one.

    • Marked as answer by JeffBurdek Monday, January 18, 2016 9:02 PM
    Monday, January 18, 2016 9:02 PM