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

如何在.NET MAUI异步方法中使用foreach避免UI卡顿?

.NET MAUI Android应用:解决大量数据添加至ObservableCollection时的UI卡顿问题

我正在开发一款.NET MAUI Android应用,现有一个方法从数据库获取数据后,通过foreach循环将数据添加到ObservableCollection中。但数据量较大时,这个过程耗时久,会导致UI卡顿冻结,想找一种不阻塞UI的优化方案。

原参考代码

ViewModel代码

private IDataService _dataService;
public ObservableCollection<Item> Items { get; set; }

public MainPageViewModel(IDataService dataService)
{
  Items = new ObservableCollection<Item>();
  _dataService = dataService;
}

public async Task LoadItemsFromDbAsync()
{
   List<Item> ItemList = await _dataService.GetItemsAsync();
   foreach(Item item in ItemList)
   {
     Items.Add(item);
   }        
}

MainPage.xaml.cs代码

private MainPageViewModel _viewModel;

public MainPage(MainPageViewModel viewModel)
{
    InitializeComponent();
    _viewModel = viewModel;
    BindingContext = _viewModel;
}

protected async override void OnNavigatedTo(NavigatedToEventArgs args)
{
   base.OnNavigatedTo(args);
   await _viewModel.LoadItemsFromDbAsync();
}

已尝试的无效方案

方案1(报错)

执行时报错:Android.Util.AndroidRuntimeException:** 'Only the original thread that created a view hierarchy can touch its views.

public ObservableCollection<Item> Items { get; set; }

public async Task LoadItemsFromDbAsync()
{
   List<Item> ItemList = await _dataService.GetItemsAsync();
   var tasks = new List<Task>();
   foreach (Item item in ItemList)
   {
      tasks.Add(Task.Run(() => AddItemToCollection(item)));
   }
  
   await Task.WhenAll(tasks);
}

private void AddItemToCollection(Item item)
{
   Items.Add(item);
}

原因:ObservableCollection属于UI绑定集合,必须在UI线程操作,Task.Run把操作放到了后台线程,违反了线程规则。

方案2(仍卡顿)

能运行但效果和原代码一致,UI依旧阻塞到全部添加完成:

public ObservableCollection<Item> Items { get; set; }

public async Task LoadItemsFromDbAsync()
{
   List<Item> ItemList = await _dataService.GetItemsAsync();
   var tasks = ItemList.Select(async item =>
   {
     Items.Add(item);
   });

   await Task.WhenAll(tasks);
}

原因:Select里的async没有实际异步操作,所有Add还是在UI线程同步执行,和原foreach没区别。

可行优化方案

方案一:分批添加+让出UI线程

把数据分成小批次,每添加一批就短暂等待,让UI线程有时间处理渲染,避免卡顿:

public async Task LoadItemsFromDbAsync()
{
   List<Item> ItemList = await _dataService.GetItemsAsync();
   int batchSize = 50; // 根据实际情况调整批次大小
   for (int i = 0; i < ItemList.Count; i += batchSize)
   {
       var batch = ItemList.Skip(i).Take(batchSize);
       foreach (var item in batch)
       {
           Items.Add(item);
       }
       // 让出UI线程处理渲染
       await Task.Delay(1);
   }
}

方案二:扩展ObservableCollection实现AddRange

ObservableCollection原生没有AddRange方法,自己扩展后可以一次性添加所有数据,只触发一次CollectionChanged事件,大幅减少UI更新次数:

// 扩展方法类
public static class ObservableCollectionExtensions
{
    public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

// ViewModel中使用
public async Task LoadItemsFromDbAsync()
{
   List<Item> ItemList = await _dataService.GetItemsAsync();
   // 先清空原有数据(如果需要)
   Items.Clear();
   // 一次性添加所有数据
   Items.AddRange(ItemList);
}

注:如果使用CommunityToolkit.Mvvm库,里面已经内置了ObservableCollectionExtensions.AddRange方法,可直接调用。

方案三:开启CollectionView虚拟化

如果页面用的是CollectionView展示数据,开启虚拟化后,UI只会渲染当前可见区域的项,即使数据量很大也不会卡顿:

<CollectionView ItemsSource="{Binding Items}"
                IsVirtualizing="True"
                VirtualizationMode="Recycling">
    <!-- 你的ItemTemplate -->
</CollectionView>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:07:51