1

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?

Cfun
  • 8,442
  • 4
  • 30
  • 62
Rajzer
  • 1,174
  • 11
  • 24
  • 1
    you just provide a function that loads the X next rows of data and adds them to your ObservableCollection. The docs explain it pretty clearly - https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/populate-data#load-data-incrementally – Jason Feb 23 '21 at 15:27
  • I can't really understand how – Rajzer Feb 23 '21 at 15:58
  • What **specifically** don't you understand? Is your service capable of accepting a parameter that allows it to return a subset of data? If not that's the first thing you need to change – Jason Feb 23 '21 at 16:00
  • @Jason i have ObservableCollection a list which is read from the API. when ```RemainingItemsThresholdReachedCommand="{Binding LoadNewPhotosCommand}" ``` is called what to do with the list? – Rajzer Feb 23 '21 at 16:16
  • load more data from your service and then add them to the ObservableCollection – Jason Feb 23 '21 at 16:21
  • @Jason i can't understand you – Rajzer Feb 23 '21 at 19:05

1 Answers1

1

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.

Jason
  • 86,222
  • 15
  • 131
  • 146
  • I have one ```ObservableCollection AllPhotos``` with 5000 data entries. And generate another ```ObservableCollection PhotoSequence``` who can get 50 items from ```AllPhotos``` everytime and Binding to the source of the CollectionView. Is that your idea? – Rajzer Feb 23 '21 at 19:15
  • no, getting the 5000 items all at once is what is making your app hang. Stop doing that. Instead use incremental loading to request them in chunks so that it is more performant. – Jason Feb 23 '21 at 19:17
  • i must get first all the items from the API. That is fast, the slow part is render all 5000 items – Rajzer Feb 23 '21 at 19:20
  • I can't understand how to get data from API in sequences. You are trying to tell me that – Rajzer Feb 23 '21 at 19:23
  • You just said that getting ALL the values from the API was fast. Regardless, your API has to support that. Typically you would specify a page size and a page number, like "/GetData?PageSize=50&PageNumber=10" - which would mean get me the next 50 rows starting with Row 500. Or you could do it by date range, or whatever makes sense for your app. – Jason Feb 23 '21 at 19:30
  • Yes but in this situation is third party API so i can't change – Rajzer Feb 23 '21 at 19:48
  • 1
    OK, then try what you suggested in the FIRST comment. – Jason Feb 23 '21 at 19:51