I have a Rest service that returns the data in pages. I know how many pages of data there is after getting the first result set. Now I want to consume this service in a WPF application, e.g. display the result in a Grid Control (or a list view).
The problem is that the whole paging mechanism should be transparent to the end user, so they shouldn't trigger data fetching by any means other than scrolling in the grid. Is this possible and how would you tackle this problem?
Here is another possible solution of your task: http://www.devzest.com/blog/post/wpf-data-virtualization.aspx
Main idea is to create your own implementation of IList which will encapsulate all async page loading features.
As a bonus that article contains full example code with a set of additional features:
- Selection, sorting and filtering works well as if all data are stored locally;
- Data loading as needed, in a separate thread, without blocking the UI;
- Visual feedback when data is loading; if failed, user can retry the last failed attempt.
Put your grid or list into the ScrollViewer, subscribe to the ScrollChanged event, then use event args properties to determine if you are close enough to the end of scrollable area, and request next page from your service, and finally add received data to the end of list or grid.
That's in short. If you need more concrete example, let me know.
EDIT: Okay, assuming you're using the System.Windows.Controls.DataGrid
control to display your data. I'm making this assumption because you've said your grid does have the scrolling capabilities built in, and no other control with name similar to grid does have it. And also because it makes sense to use DataGrid for data display. :)
You declare your DataGrid like this:
<DataGrid HorizontalAlignment="Left" Margin="20,10,0,0"
VerticalAlignment="Top" Height="301" Width="498"
ScrollViewer.ScrollChanged="DataGrid_ScrollChanged_1"
ItemsSource="{x:Static Fonts.SystemFontFamilies}">
</DataGrid>
Notice that I'm using ScrollViewer.ScrollChanged
routed event. This is possible because DataGrid indeed has the ScrollViewer built in. This means it's possible to subscribe to that event and analyze it's arguments.
Here's how I handle this event for testing purposes:
private void DataGrid_ScrollChanged_1(object sender, ScrollChangedEventArgs e)
{
Debug.WriteLine("Extent height: " + e.ExtentHeight +
", vertical offset: " + e.VerticalOffset +
", viewport height: " + e.ViewportHeight);
}
When my datagrid is scrolled to the top, I see the following output:
Extent height: 267, vertical offset: 0, viewport height: 13
When it's scrolled to the bottom:
Extent height: 267, vertical offset: 254, viewport height: 13
So it's quite easy to determine when you're close to the bottom and act accordingly:
const int threshold = 20;
if (e.ExtentHeight <= e.VerticalOffset + e.ViewportHeight + threshold)
{
AskForNextPage();
}
Of course, there are some nuances here. You need to keep track if you've already downloading some page, and how many pages you've already loaded, to avoid data duplication and other inconsistencies. And, honestly speaking, this will be the hardest part to do, compared to what I've written here. :)
I thought the problem was interesting but the answer is too long for stackoverflow window so I built a simple app that uses a prefetching collection view.
It's sort of similar approach as the one posted by Woodman.
https://github.com/mrange/CodeStack/tree/master/q14793759/AutoFetching
The interesting code is in the class: PrefetchingCollectionView