Search code examples
c#xamarin.formsxamarin-community-toolkitxamarin.forms.collectionview

Xamarin.Forms implement Lazy Loading in CollectionView


This is my CollectionView xaml file:

    <ContentPage.Content>
        <Grid>
            <StackLayout>
                <RefreshView IsRefreshing="{Binding IsRefreshing, Mode=OneWay}"
             Command="{Binding LoadRefreshCommand}">
                    <CollectionView ItemsSource="{Binding Photos}" SelectedItem="{Binding SelectedPhoto}" RemainingItemsThreshold="10" RemainingItemsThresholdReachedCommand="{Binding LoadNewPhotosCommand}" >
                        <CollectionView.ItemTemplate>
                            <DataTemplate>
                                <Frame BorderColor="#3498DB">
                                    <Grid  RowSpacing="0" ColumnSpacing="0" Padding="0">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*"/>
                                            <ColumnDefinition Width="3*"/>

                                        </Grid.ColumnDefinitions>
                                        <Grid.GestureRecognizers>
                                            <TapGestureRecognizer  Command="{Binding Source={x:Reference CurrentPage}, Path=BindingContext.LoadPhotosCommand}" />
                                        </Grid.GestureRecognizers>
                                        <Image Aspect="AspectFit" HeightRequest="50" Source="{Binding Url}"  Grid.Column="0"></Image>
                                        <Frame HasShadow="False" VerticalOptions="Center"  Grid.Column="1">
                                            <Label Text="{Binding Title}"></Label>
                                        </Frame>
                                    </Grid>
                                </Frame>
                            </DataTemplate>
                        </CollectionView.ItemTemplate>
                    </CollectionView>
                </RefreshView>
            </StackLayout>
            <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center">
                <Frame IsVisible="{Binding IsBusy}" BorderColor="#3498DB" HasShadow="False" BackgroundColor="#eeeeee">
                    <StackLayout>
                        <ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" HorizontalOptions="Center" VerticalOptions="Center"></ActivityIndicator>
                        <Label TextColor="#3498DB" Text="Loading Data, Please Wait..." HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center" IsVisible="{Binding IsBusy}"/>
                    </StackLayout>
                </Frame>
            </StackLayout>
        </Grid>
    </ContentPage.Content>

This is the constructor in the ViewModel:

        public MainPageViewModel()
        {
            LoadPhotosCommand = new Command(execute: async () => await ExecuteGetAllPhotos());
            LoadRefreshCommand = new Command(execute: async () => await ExecuteRefreshGetAllPhotos());
            LoadRefreshCommand = new Command(execute: async () => await ExecuteGetNewPhotos());
            Photos = new ObservableCollection<Photo>();
            PhotosModel = new PhotosModel();
            SelectedPhoto = new Photo();
        }

This is the code in the ViewModel how i get the data:

        async Task ExecuteGetAllPhotos()
        {
            if (IsBusy)
                return;      
            try
            {
                IsBusy = true;
                Photos.Clear();
                PhotosModel = await InstagramCloneDataStore.GetAllPhotos();
               if(PhotosModel!=null)
                {
                    foreach (var photo in PhotosModel.Photos)
                    {
                       Photos.Add(photo);                      
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                var msg = ex.Message;
            }
            finally
            {
                IsBusy = false;
            }
        }

This code work perfectly fine when a read 100,200,500 data entries.But when i read 5000 data entries the app got freezed and want to make a force stop. It wait about 2-3minutes to show the data. So i want to implement some logic for Lazy loading or Infinity scrolling. This is the command functionfor RemainingItemsThresholdReachedCommand and logic should be writen for scrolling data:

        async Task ExecuteGetNewPhotos()
        {
            if (IsBusy)
                return;
            try
            {
//some logic here---------->
                IsBusy = true;
                Photos.Clear();
                PhotosModel = await InstagramCloneDataStore.GetAllPhotos();
                if (PhotosModel != null)
                {
                    foreach (var photo in PhotosModel.Photos)
                    {          
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                var msg = ex.Message;
            }
            finally
            {
                IsBusy = false;
            }
        }

I don't understand how lazy loading or scrolling working. Can someone help?


Solution

  • When your page loads, you should call your service to load the first X rows of data. Let's say that X=50 - it can be whatever value you want.

    When the user scrolls to the bottom of the list, CollectionView will fire RemainingItemsThresholdReachedCommand. When this fires, you should fetch the next X number of items from your service and add them to your ItemsSource.

    For this to work, your service needs to be capable of incrementally loading data.