WinRT: How to properly implement ISupportIncrementalLoading with navigation

BestComparator is about to publish its new Windows 8 application, and I ran into a really weird and frustrating issue when implementing ISupportIncrementalLoading.

Supporting infinite scrolling pagination

The best way to support infinite scrolling, is to use a GridView or a ListView and bind it an ObservableCollection that implements the ISupportIncrementalLoading.

You will probably end with a class like this one:

public class PaginatedCollection : ObservableCollection, ISupportIncrementalLoading
{
    private Func<uint, Task<IEnumerable>> load;
    public bool HasMoreItems { get; protected set; }

    public PaginatedCollection(Func<uint, Task<IEnumerable>> load)
    {
        HasMoreItems = true;
        this.load = load;
    }

    public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
    {
        return AsyncInfo.Run(async c => {
            var data =  await load(count);

            foreach (var item in data) {
                Add(item);
            }

            HasMoreItems = data.Any();

            return LoadMoreItemsResult() {
                Count = data.Count,
            };
        });
    }
}

Then you can simply create a bindable collection for your GridView or ListView in you ViewModel this way:

// in your view model
Items = new PaginatedCollection(async count => {
    var result = // your logic to load more items asynchronusly
    return result;
});

At binding time, the control will automatically detect that you binded a ISupportIncrementalLoading collection and will automatically call the LoadMoreItemsAsync when needed.

The incomprehensible AccessViolationException

Everything seems to work fine but if you happen to navigate in and out of a page using this interface you might get this exception for apparently no reason: AccessViolationException. You will get no stack trace to debug. Basically, the platform crash at a very low level around the C++ layer. Apparently, the asynchronous loading get bugged when you leaved the page and it is destroyed.

The almost easy workaround

I was stuck on this issue for quite a long time. I event though of manually implementing the infinite scrolling the way I did it in wp7, which I was not found of because it was quite hack (I was listening to the compress state of the scroller in order to detect when the end of the list was reached). Then I got the idea of caching the pages at a low level using the NavigationCacheMode of the Page control. It is simply done in the LayoutAwarePage constructor:

public LayoutAwarePage()
{
    NavigationCacheMode = NavigationCacheMode.Enabled;
    // the rest of the logic…
}

By doing so, you tell the framework to keep the page in the memory and directly use it when navigating forth and back to the page. This way, because the page still lives in memory, your asynchronous request to get more items continues to behave properly when navigating.

The hidden issue behind page caching

But, the fix is not complete yet. By using page caching, it is not properly regenerated when you navigate to a page already loaded but with a different context. Let’s take an example: BestComparator has a homepage listing different categories of products. When selecting the smartphones category, you navigate to a CategoryPage with the id of the category as a parameter. This was the smartphones category is completely fetched with its corresponding products. But if you go back to the homepage and then navigate to the laptops category, the framework will use the previously cached CategoryPage which was loaded with smartphones… and won’t trigger the load of the laptops.

The fix here is quite subtle, what you need to do is to trigger the LoadState procedure not only when the page is new, but also when navigating forward, mimicking the windows phone 7 navigation system. Just go to the OnNavigatedTo method of the LayoutAwarePage and add in the first line a test concerning the NavigationMode:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Returning to a cached page through navigation shouldn't trigger state loading
    if (this._pageKey != null && e.NavigationMode == NavigationMode.Back) return;
    // The rest of the logic...
}

And here it is, you completely support infinite scrolling with no bug, and won a nice page caching strategy on the way.

Wrap up

The new interface ISupportIncrementalLoading provided in WinRT can be very useful but its behavior when asynchronously loading items and navigating is quite bugged. What you have to do is activate a Page caching strategy and don’t forget to tweak the loading state process and everything should run smoothly.

10 thoughts on “WinRT: How to properly implement ISupportIncrementalLoading with navigation

    1. Most or the time you will want to make a web request. If you dig more into that you will see that the framework will make you use async function that enforce the parallelization of the request.

  1. Thanks Michel for the fast response =)

    OK I think its starting to make sense. I am coding a test app in an effort to try and understand the mechanisms, …

    Items = new PaginatedCollection(async count =>
    {
    var result = // say web request, say first 50 items

    return result;
    });

    Then the object (PaginatedCollection) which implements ISupportIncrementalLoading , LoadMoreItemsAsync would load the next set of data (providing the HasMoreData flag is true). For the base, my ObservableCollection is a list of Person objects:

    public class IncrementalPerson : ObservableCollection, ISupportIncrementalLoading

    Unfortunately I keep getting:

    The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

    Am I making a rookie mistake?

  2. Yup, was updating the UI from the wrong thread. I have it working now, although still trying to figure out how it works exactly so that I can plan an incorporate the incremental loading stuff into my solution. Thanks a lot for this article =)

  3. Wondering if it possible to set a variable in the PaginatedCollection class from the view model, i.e.

    incrementalDataGrid.ItemsSource = new IncrementalPerson(async count => {
    ServerResponse response = await api.GetEmployees(50, token);
    return response.Data;
    });

    // works and gives me the first set of employee person objects but my problem is that the response has a field “NextPageURL” with has information how to query our DB for the next set of data. I have a field for this in the ObservableCollection class and could easily use if from LoadMoreItemsAsync but how do I initially set that field?

    incrementalDataGrid.ItemsSource = new IncrementalPerson(async count => {
    ServerResponse response = await api.GetEmployees(50, token);

    // response has a next page URL string, can I assign it to a field in
    // in the PaginationCollection class from here? like
    this.nextPageURL = response.NextPageURL; <<<< ????

    return response.Data;
    });

  4. Hello, using your code, i managed to use the incremental loading. There is only one small problem. After i load the listview with items, and i scroll down a bit, when i try to initialize again the data source, the scrollbar is set to the start position but the first item shown is the 20th in the list. Could you please see if this happens to you also? Thank you

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s