You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms CollectionView无限滚动分页加载报错问题

问题原因

你遇到的System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'异常,核心原因有两个:

  1. 异步拉取数据完成后,直接在非UI线程修改绑定的ObservableCollection,此时UI线程正在遍历集合渲染列表,两个操作同时触发就会报枚举冲突错误
  2. 分页逻辑缺少终止判断,滚动到底后会重复触发无效加载请求,进一步加剧集合操作冲突

你现在用的RemainingItemsThreshold和RemainingItemsThresholdReachedCommand是CollectionView官方原生提供的无限滚动实现,不需要额外引入适配ListView的Xamarin.Forms.Extended.InfiniteScrolling扩展,性能比监听ListItemAppearing事件的方案更好。

修复方案

第一步:完善ViewModel分页逻辑

核心要做三个调整:加最后一页标记、所有集合修改操作切到UI线程执行、补全异常和状态控制,修正后的代码如下:

// 全局字段定义
private const int pageSize = 20;
private bool _isBusy;
private bool _isLastPage; // 标记是否已加载完全部数据
private readonly ObservableCollection<Cart> _oListCart = new ObservableCollection<Cart>();

// 列表绑定属性
public ObservableCollection<Cart> ListCart => _oListCart;

public bool IsBusy
{
    get => _isBusy;
    set
    {
        _isBusy = value;
        OnPropertyChanged(); // 替换为你自己MVVM框架的属性通知实现
    }
}

// 加载更多执行方法
private async void LoadMore()
{
    // 正在加载、已加载完全部数据时直接返回,避免重复请求
    if (IsBusy || _isLastPage)
        return;

    try
    {
        IsBusy = true;
        // 计算当前页码:如果你的API第一页页码从1开始,就改为 ListCart.Count / pageSize + 1
        int currentPage = ListCart.Count / pageSize;
        
        // 调用分页接口
        var carts = await GetListCart(pageSize, currentPage);
        
        // 关键:切到UI线程再修改绑定集合,避免和UI渲染的枚举操作冲突
        Device.BeginInvokeOnMainThread(() =>
        {
            foreach (var cart in carts)
            {
                _oListCart.Add(cart);
            }
        });

        // 判断是否到最后一页:本次返回数据量小于页大小,说明没有更多数据
        if (carts.Count < pageSize)
        {
            _isLastPage = true;
        }
    }
    catch (Exception ex)
    {
        // 自行处理加载失败的提示逻辑
        await App.Current.MainPage.DisplayAlert("提示", $"加载失败:{ex.Message}", "确定");
    }
    finally
    {
        IsBusy = false;
    }
}

// 页面首次加载初始化方法
public async Task InitPageData()
{
    _oListCart.Clear();
    _isLastPage = false;
    await LoadMore();
}

在页面的OnAppearing生命周期里调用一次InitPageData,就能实现首次加载前20条数据的需求。

第二步:确认XAML配置

你现有的XAML配置基本正确,不需要额外修改,参考如下:

<CollectionView x:Name="ListViewCart" 
                ItemsSource="{Binding ListCart}" 
                RemainingItemsThreshold="4"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}"
                VerticalScrollBarVisibility="Always"
                SelectionMode="Single"
                SelectedItem="{Binding CartSelected}"                      
                SelectionChangedCommand="{Binding ShowAlertCommand}"
                SelectionChangedCommandParameter="{Binding CartSelected, Source={RelativeSource Self}}">
    <!-- 这里保留你原有的ItemTemplate等布局配置 -->
</CollectionView>

RemainingItemsThreshold="4"表示滚动到距离列表末尾还有4个条目时自动触发加载命令,这个值可以根据你的需求调整,数值越大预加载触发时机越早,滚动流畅度越高。

注意:如果你的分页接口是按「跳过N条」的规则传参,只需要把参数计算改成int skipCount = ListCart.Count,传入接口对应参数即可,其余逻辑不用调整。

内容的提问来源于stack exchange,提问作者user18972374

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 17:06:29