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

WPF DataGrid能否直接绑定IAsyncEnumerable数据源?如何实现?

WPF中直接使用IAsyncEnumerable作为DataGrid数据源的实现方案

WPF的ItemsControl系列控件(包括DataGrid)原生仅支持同步枚举类型的数据源,无内置的IAsyncEnumerable适配逻辑,直接赋值作为ItemsSource不会触发数据加载和渲染。
以下方案可以完全保留IAsyncEnumerable流式加载的优势,无需等待全量数据返回即可逐步渲染UI:

核心实现思路

异步迭代IAsyncEnumerable返回的元素,每拿到一个/一批元素就封送到UI线程,添加到ObservableCollection中,利用ObservableCollection的集合变更通知触发DataGrid增量刷新。

实现代码

1. 基础版扩展方法(单条增量加载)

需要先安装NuGet包System.Linq.Async

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

public static class AsyncEnumerableExtensions
{
    /// <summary>
    /// 将IAsyncEnumerable转为增量加载的ObservableCollection
    /// </summary>
    /// <param name="asyncSource">异步数据源</param>
    /// <param name="uiDispatcher">UI线程Dispatcher</param>
    /// <param name="cancellationToken">取消令牌</param>
    public static async Task<ObservableCollection<T>> ToIncrementalObservableCollectionAsync<T>(
        this IAsyncEnumerable<T> asyncSource, 
        Dispatcher uiDispatcher,
        CancellationToken cancellationToken = default)
    {
        var resultCollection = new ObservableCollection<T>();
        
        await foreach (var item in asyncSource.WithCancellation(cancellationToken))
        {
            // 封送回UI线程执行集合修改操作
            uiDispatcher.Invoke(
                () => resultCollection.Add(item), 
                DispatcherPriority.Background);
        }

        return resultCollection;
    }
}

2. 优化版扩展方法(批量增量加载)

数据量较大时推荐使用,减少UI线程调度次数,降低渲染开销:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

public static class AsyncEnumerableExtensions
{
    /// <summary>
    /// 将IAsyncEnumerable转为批量增量加载的ObservableCollection
    /// </summary>
    /// <param name="asyncSource">异步数据源</param>
    /// <param name="uiDispatcher">UI线程Dispatcher</param>
    /// <param name="batchSize">每批加载的元素数量</param>
    /// <param name="cancellationToken">取消令牌</param>
    public static async Task<ObservableCollection<T>> ToIncrementalObservableCollectionAsync<T>(
        this IAsyncEnumerable<T> asyncSource, 
        Dispatcher uiDispatcher,
        int batchSize = 10,
        CancellationToken cancellationToken = default)
    {
        var resultCollection = new ObservableCollection<T>();
        var tempBatch = new List<T>(batchSize);
        
        await foreach (var item in asyncSource.WithCancellation(cancellationToken))
        {
            tempBatch.Add(item);
            if (tempBatch.Count >= batchSize)
            {
                uiDispatcher.Invoke(() => 
                {
                    foreach (var batchItem in tempBatch)
                    {
                        resultCollection.Add(batchItem);
                    }
                }, DispatcherPriority.Background);
                tempBatch.Clear();
            }
        }

        // 加载最后一批不足阈值的元素
        if (tempBatch.Count > 0)
        {
            uiDispatcher.Invoke(() => 
            {
                foreach (var batchItem in tempBatch)
                {
                    resultCollection.Add(batchItem);
                }
            }, DispatcherPriority.Background);
        }

        return resultCollection;
    }
}

用法示例

后台代码直接调用示例

// 窗口加载事件逻辑
private async void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
    // 先绑定空集合,提前渲染UI结构
    var itemCollection = new ObservableCollection<YourDataModel>();
    MyDataGrid.ItemsSource = itemCollection;

    // 异步拉取数据并增量加载,无需等待全量返回
    await FetchDataFromDbAsync() // 该方法返回IAsyncEnumerable<YourDataModel>
        .ToIncrementalObservableCollectionAsync(Dispatcher);
}

MVVM模式调用示例

在ViewModel中定义属性:

public ObservableCollection<YourDataModel> Items { get; set; } = new();

public async Task LoadDataAsync(CancellationToken cancellationToken)
{
    await FetchDataFromDbAsync()
        .ToIncrementalObservableCollectionAsync(
            Application.Current.Dispatcher, 
            cancellationToken: cancellationToken);
}

XAML中绑定:

<DataGrid ItemsSource="{Binding Items}" x:Name="MyDataGrid"/>

注意事项

  • 保持DataGrid的UI虚拟化功能开启(默认配置已开启),即使加载上万条数据也不会出现卡顿
  • 可以结合加载状态标识,在枚举开始时显示加载中状态,枚举结束后隐藏,提升交互体验
  • 无需额外实现IObservable接口,上述方案为轻量原生实现,完全保留IAsyncEnumerable的流式加载特性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:39:03