Modern mobile applications are mostly data driven apps that depend heavily on listview’s. With the increased amount of data a lazy loading mechanism is required in this article, we will explain how to build a lazy loaded list view easily.
As an example I will build a last fm app that return a list of albums for an artist.
I started by creating a new xamarin app and then install the following nuget packages
- Inflatable.Lastfm
- Syncfusion.Xamarin.SfListView
- AsyncEnumerableExtensions.Standard
Now let's create our view model
public class MainPageViewModel
{
private LastfmClient _client;
public MainPageViewModel()
{
_client = new LastfmClient("b471c30029ba983849723e5120e7504c", "24c4739698eebb92f792b118095dbdff");
Artists = GetArtists().ToStandardIIncrementelLoadingCollection(10);
}
public IIncrementelLoadingCollection<AlbumModel> Artists { get; }
private IAsyncEnumerable<AlbumModel> GetArtists()
{
return AsyncEnumerableBuilder.FromPaged((i, i1) =>
_client.Artist.GetTopTracksAsync("Britney Spears", true, i, i1),
tracks => tracks.Select(t =>
new AlbumModel()
{
Name = t.Name,
Id = t.Id,
Image = t.Images.Large.ToString(),
Duration = t.Duration
}),
(i, tracks) => tracks.TotalItems > i,
(tracks, i) => tracks.Count() + i,
tracks =>
{
var pagenumber = (tracks?.Page ?? 0) + 1;
return pagenumber;
},
tracks => 20);
}
}
}
The method AsyncEnumerableBuilder.FromPaged
was described in detail in my previous article "Mobile paged requests using IAsyncEnumerable" as a fast summary it allow building an IAsyncEnumerable
from a method that return paged results which is "GetTopTracksAsync
" in our case .
The method ToStandardIIncrementelLoadingCollection
allow the conversation from IAsyncEnumerable
to a bindable incremental collection that expose this interface
public interface IIncrementelLoadingCollection<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
ICommand LoadMoreCommand { get; }
bool IsBusy { get; }
}
The integer parameter is the number of items that will get loaded every time the user tap on load more , in our example it is 10 "ToStandardIIncrementelLoadingCollection(10)
"
Now finally the view
<StackLayout BindingContext="{Binding Path=Artists}">
<xForms:SfListView x:Name="listView"
ItemSpacing="10"
LoadMoreOption="Manual"
LoadMoreCommand="{Binding LoadMoreCommand }"
IsBusy="{Binding IsBusy}"
ItemsSource="{Binding }">
<xForms:SfListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Spacing="10">
<Image Source="{Binding Image}" HeightRequest="100" WidthRequest="100" />
<Label Text="{Binding Name}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</xForms:SfListView.ItemTemplate>
<xForms:SfListView.LoadMoreTemplate>
<DataTemplate>
<Grid>
<Label Text="Load More Items" TextColor="Black" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" IsVisible="{Binding IsBusy, Converter={StaticResource inverseBoolConverter}, Source={x:Reference Name=listView}}" />
<xForms:LoadMoreIndicator IsRunning="{Binding IsBusy, Source={x:Reference Name=listView}}" IsVisible="{Binding IsBusy, Source={x:Reference Name=listView}}" Color="Red" VerticalOptions="Center"/>
</Grid>
</DataTemplate>
</xForms:SfListView.LoadMoreTemplate>
</xForms:SfListView>
</StackLayout>
The core part is
<StackLayout BindingContext="{Binding Path=Artists}">
<xForms:SfListView x:Name="listView"
ItemSpacing="10"
LoadMoreOption="Manual"
LoadMoreCommand="{Binding LoadMoreCommand }"
IsBusy="{Binding IsBusy}"
ItemsSource="{Binding }">
we set the binding context of the listview to Artists which is our IIncrementelLoadingCollection
, the LoadMoreCommand
to the Artist.LoadMoreCommand
and IsBusy
to Artist.IsBusy
and finally the data source to the Artists itself.