visual-studiomauicollectionviewsource

CollectionView ThresholdReachedCommand fired before loading complete


Has a question regarding CollectionView in MAUI. So, I have CollectionView wrapped to RefreshView.

<RefreshView Grid.Row="1"
             Command="{Binding RefreshSearchCommand}"
             IsRefreshing="{Binding IsBusy}">
    <CollectionView ItemsSource="{Binding Requests}" x:Name="RequestCollection"
                RemainingItemsThreshold="1"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreDataCommand}">
    <CollectionView.Footer>
        <ActivityIndicator IsRunning="True" IsVisible="{Binding IsLoading}"/>
    </CollectionView.Footer>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="model:GetRquestsAndUser">
            <Cntr:ReqSinglPanel
                CurrentRequest="{Binding .}"
                Padding="0,5,5,5"/>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView> </RefreshView>

Also, have a RelayCommand for RefreshView

    public void RefreshSearch()
    {
        if (IsLoading)
            return;
        page = 0;
        Requests.Clear();
        try
        {
            Search();
        }
        catch (Exception ex) { ...... }
        finally {
            IsBusy = false;
            IsLoading = false;
        }
    }

And RelayCommand for CollectionView

public void LoadMoreData()
        {
            if (IsLoading || IsBusy)
                return;           
            try { 
                IsLoading = true;
                Search();
            }
            catch(Exception ex) { .... }
            finally { IsLoading = false; }
        }

So, on screen load, I set refreshView in load mode IsBusy=true. It goes through the Search() and load data into public ObservableCollection<GetRquestsAndUser> Requests But before the data appear in collectionView it has enough delay to fire RemainingItemsThresholdReachedCommand and go through the loading process again, load extra data, and even double already loaded data(don't know yet how).

Actually, the same happens when RemainingItemsThresholdReachedCommand fires the normal way, it sets IsLoading=false and fires yourself again because RemainingItemsThreshold is still lower than 1.

Is it a possible way to prevent such behavior? Or might be another way to make it work properly?

I'm thinking to get rid of (RefreshView) / (Threshold part of CollectionView), but not sure (how to implement the "pull up to refresh" functionality only with CollectionView) / (dynamic loading only with RefreshView)

UDP:

Search code
public void Search()
        {
                Task.Run(async () =>
                {
                    try{
                        Global.searchModel.Page = page;
                        SearchResult = await _restService.PostGetRquestsAndUser(Global.searchModel);                  
                        foreach(GetRquestsAndUser req in SearchResult.Result)
                            Requests.Add(req);
                        SearchCountStr = string.Format("{0} out of {1}", Requests.Count.ToString(), SearchResult.SearchCount.ToString());
                        page++;
                    }
                    catch (Exception ex) {}
                });
        }

Solution

  • First, yes. It makes sense for the command to be async. At least all my communications are done in such commands, and are awaited.

    Second, ObservableCollection is terrible class for what you are doing. One of the first things that I do, is make RangeObservableCollection, that allows me to modify the list of items, without raising notifications for every item. (Usually, I either block notifications, and allow them after the adding is done, with internal check if any modifications have been done... or I simply add Silent methods, that modify the Items. AddSilent and AddRangeSilent for example..)

    Third, your clearing of the collection makes no sense. It is a loop. Add Items > Go to the end > Clear Items > Add Items > Go to the end ...

    This command should be used for loading more items , when you detect you are reaching the end. Not for resetting all at the beginning.

    In fact, your idea for "pull to refresh" is the most common approach to solve your problem. And your users are familiar with it. Just go for it.