Incremental, On-Demand Loading with Xamarin.Forms ListView
On-demand loading is a common technique in mobile apps where data is loaded in chunks as the user scrolls down a list in real time. If you’ve ever searched for something to buy on the Amazon shopping app, then you’ve seen on-demand loading. It’s ideal for mobile apps because downloading data conservatively improves app performance, and users prefer downloading only what they need. In the development world, it’s also known as incremental loading. If you’re developing a mobile app using Xamarin.Forms, you'll find that on-demand loading is not supported for free—you have to implement it yourself.
Here, we'll show you how to use the ComponentOne CollectionView for Xamarin to provide on-demand loading for the Xamarin.Forms ListView control.
How to Perform On-Demand Loading with the Xamarin.Forms ListView
Adding on-demand loading to ListView is accomplished in two basic steps:
- Implement your own collection view class that extends a C1CursorCollectionView and overrides GetPageAsync. This is where we'll provide our logic that loads the data in chunks or pages.
- Extend ListView and listen to the ItemAppearing event. This determines when the user has reached the bottom of the ListView
Next, let’s cover both steps in more detail.
Extending C1CursorCollectionView
The C1CursorCollectionView is the base class for adding cursor-like, incremental loading to a collection of items. It gets its name from the similarly named paging mechanism used by the Twitter API. It provides the helpful hooks to load data in “real time” as paged chunks. First, add the C1.CollectionView packages to your Xamarin.Forms app.
Right-click your solution and select Manage NuGet Packages for Solution. Then search or browse the list for C1.CollectionView. Click Install.
The C1CollectionView is a portable class library , so it should work across all projects within your Xamarin.Forms solution. It contains multiple types of collection views. Create a new class that extends the C1CursorCollectionView base class. Here's the bare bones implementation of this class and the GetPageAsync method.
**public** **class** MyOnDemandCollectionView : C1CursorCollectionView<MyDataItem>
{
**protected** override async Task<Tuple<string, IReadOnlyList<MyDataItem>>> GetPageAsync(string pageToken, **int**? count = null)
{
// create new page of items
var newItems = **new** List<MyDataItem>();
**for**(**int** i = 0; i < count; i++)
{
newItems.Add(**new** MyDataItem(**this**.Count + i));
}
**return** **new** Tuple<string, IReadOnlyList<MyDataItem>>("token not used", newItems);
}
}
Inside the GetPageAsync override is where you'll provide your logic of downloading or returning a sub-section of the data. You may be using a web service or a local database. (For a more complete sample, see the end of this article.) The sample implementation of GetPageAsync above does not have any async operations within it that you'll likely have in a real-world scenario. Here, I'm just generating new data items as the user scrolls in order to clearly show how GetPageAsync works. Let’s talk about the parameters. Page Token—the page token can be of any type.
It’s useful for passing some value to each subsequent call of the GetPageAsync method. In my simplified version above, it’s not used. I'm using this.Count (not to be confused with the count parameter) as a token to know where to start the new collection. The last line of code passes a useless string where you may pass a value to use in the next call. Count—this is optionally passed in from the UI control, and it represents the total number of items in each page. It works exactly like the count parameter in the ISupportIncrementalLoading interface from WinRT. Consider it the desired page size. If you have no control over the page size, you can ignore this. You could alternatively manage your own page size property in your collection view implementation depending on if, and how, requesting pages of data from your data source is supported. For my sample, I'll manage the page size on my ListView extension covered later, so I'll be using the count parameter passed in from the ListView.
See the end of this article for more information.
Extending Xamarin.Forms ListView
When loading items on demand, we can’t do it without some involvement from the UI control. In this case, we’re using a ListView. We need to know when the user has reached near the bottom of the list and trigger a request for a new page of items. For this, we can listen to the ItemAppearing event. Add a class named ListViewEx with a static extension named LoadItemsOnDemand (or anything). Somewhere within the ItemAppearing handler you need to call LoadMoreItemsAsync on the collection view. In the example below it's called twice: upon initially calling LoadItemsOnDemand, and then again every time the user reaches near the end of the ListView (index == count). I’ve also added a PageSize property here so I can adjust it on my view. The PageSize is passed to GetPageAsync as the count parameter discussed above.
**public** **static** **class** **ListViewEx**
{
**private** **static** **int** PageSize { **get**; **set**; }
**public** **static** **void** **SetPageSize**(**this** ListView listView, **int** pageSize)
{
PageSize = pageSize;
}
**public** **static** **void** LoadItemsOnDemand<T>(**this** ListView listview, C1CursorCollectionView<T> collectionView)
{
listview.ItemAppearing += (s, e) =>
{
**var** index = collectionView.IndexOf((T)e.Item);
**if** (index == collectionView.Count - 1)
{
**if** (collectionView.HasMoreItems)
{
collectionView.LoadMoreItemsAsync(PageSize);
}
}
};
**if**(collectionView.HasMoreItems)
{
collectionView.LoadMoreItemsAsync(PageSize);
}
}
}
Make sure to make the class and methods static, and since the first parameter is a ListView, it acts like an extension method that we can easily call from our view. Keep in mind that you don’t have to manage the page size here. You could call collectionView.LoadMoreItemsAsync() with no parameters, and then hard-code or manage the page size on your custom collection view.
You’ll see in the other samples we provide this is sometimes how it’s done. Finally, we can use both the collection view and the ListView extension. I'll skip over setting up the ListView control. Below is the code for your view or view model that instantiates the new collection view, populates it to your ListView, and calls the extension methods on the ListView to set the on-demand loading.
// instantiate our on demand collection view
MyOnDemandCollectionView myCollectionView = new MyOnDemandCollectionView();
MyListView.ItemsSource = myCollectionView;
// **set** page **size**
MyListView.SetPageSize(10);
// **start** **on** **demand** loading
MyListView.LoadItemsOnDemand(myCollectionView);
You can tell items are being created on demand due to the time stamps. As I mentioned earlier, ComponentOne is a licensed library. If you do plan to use the C1 CollectionView you should purchase a license for Studio Enterprise. You’ll also get other controls like charts and gauges, so it’s worth it. You can download the complete sample used in this blog post below.
Download On-Demand CollectionView Sample >>> You can also find more samples, such as a more real-world sample that queries videos from YouTube on demand on GitHub.