Xamarin Forms CollectionView无限滚动分页加载报错问题
问题原因
你遇到的System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'异常,核心原因有两个:
- 异步拉取数据完成后,直接在非UI线程修改绑定的
ObservableCollection,此时UI线程正在遍历集合渲染列表,两个操作同时触发就会报枚举冲突错误 - 分页逻辑缺少终止判断,滚动到底后会重复触发无效加载请求,进一步加剧集合操作冲突
你现在用的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
相关产品推荐
相关产品推荐

