如何高效填充WPF DataGrid?去除ToList实现DbSet流式加载
问题描述
我使用WPF DataGrid展示数据,目前通过DbSet调用ToList()方法填充数据源,加载速度很慢。想知道能不能去掉ToList(),实现DbSet数据流式传输到DataGrid,避免一次性把所有数据加载到内存列表里。相关代码示例如下:
DB上下文代码
using MyNameSpace.Data.DataModels; using Microsoft.EntityFrameworkCore; namespace MyNameSpace.Data { public class DB: DbContext { protected override void OnConfiguring(DbContextOptionsBuilder options_builder) { options_builder.UseSqlServer("Server=server;Database=database;TrustServerCertificate=true;Integrated Security=true"); } public DbSet<Projects> Projects { get; set; } } }
主窗口代码
using MyNameSpace.Data; using MyNameSpace.Data.DataModels; using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace MyNameSpace { public partial class MainWindow : Window { List<Projects> projects = new List<Projects>(); public MainWindow() { InitializeComponent(); } private void RetrieveButton_Click(object sender, RoutedEventArgs e) { GetProjects(); } public void GetProjects() { var db = new DB(); projects = db.Projects.ToList(); ProjectsDatagrid.ItemsSource = projects; } } }
解决方案
完全可以去掉ToList()实现流式加载,核心是利用EF Core的延迟查询特性,配合WPF DataGrid的虚拟化机制,让数据按需加载,而非一次性全量入内存。
1. 直接绑定DbSet到DataGrid
DbSet本身是IQueryable<T>类型,支持延迟执行,无需转成List<T>就能直接绑定:
public void GetProjects() { var db = new DB(); // 直接绑定DbSet,跳过ToList() ProjectsDatagrid.ItemsSource = db.Projects; }
这样EF Core不会一次性拉取所有数据,只有当DataGrid需要渲染某几行时,才会从数据库加载对应的数据——前提是你开启了DataGrid的虚拟化。
2. 开启DataGrid虚拟化(关键)
WPF DataGrid默认可能未启用虚拟化,必须开启才能实现真正的按需加载。在XAML中给DataGrid添加以下属性:
<DataGrid x:Name="ProjectsDatagrid" EnableRowVirtualization="True" VirtualizingStackPanel.VirtualizationMode="Recycling" EnableColumnVirtualization="True"/>
EnableRowVirtualization="True":只渲染可见区域的行,减少UI渲染压力VirtualizationMode="Recycling":复用行容器,避免频繁创建销毁UI元素EnableColumnVirtualization="True":如果列数量较多,开启列虚拟化进一步优化
3. 重要注意事项
- DbContext生命周期管理:不要在方法内创建DbContext后立即销毁(比如原代码中
GetProjects方法内的db会在方法结束后被回收),否则DataGrid后续加载数据时会报错。建议把DbContext声明为窗口级字段:
private DB _db; public MainWindow() { InitializeComponent(); _db = new DB(); } public void GetProjects() { ProjectsDatagrid.ItemsSource = _db.Projects; } // 窗口关闭时释放DbContext protected override void OnClosed(EventArgs e) { base.OnClosed(e); _db.Dispose(); }
- 避免触发全量加载:不要对
_db.Projects调用Count()、ToList()这类会立即执行查询的方法,否则还是会一次性加载所有数据。 - 超大数据量的替代方案:如果数据量极大,流式加载仍有压力,可以结合分页查询,用
Skip()和Take()配合分页控件手动控制加载量:
// 示例:加载第1页,每页20条 ProjectsDatagrid.ItemsSource = _db.Projects.Skip(0).Take(20).ToList();
内容的提问来源于stack exchange,提问作者Dunno
相关产品推荐
相关产品推荐

