WPF+Prism异步从SQL Server取数据绑定DataGrid界面不显示问题
Prism WPF异步加载数据到DataGrid无显示问题解决
问题场景
在Prism框架的WPF应用中,同步方式从SQL Server仓储层获取数据并绑定到DataGrid时,数据能正常显示;但切换为异步方式后,程序无报错,却无法在界面显示数据。尝试在ViewModel构造函数中调用异步私有方法加载数据,问题仍未解决。
相关代码
视图代码
<UserControl> <Grid> <DataGrid ItemsSource="{Binding Products}" /> </Grid> </UserControl>
ViewModel代码(原问题版本)
public class ProductViewModel : BindableBase, INavigationAware { private readonly IProductRepo _repo = new ProductRepo(); public ObservableCollection<Data.Models.Product> Products { get; set; } public ProductViewModel() { Products = new ObservableCollection<Data.Models.Product>(this._repo.GetAll().Result); } }
DbContext代码
public class ApplicationDbContext : DbContext { public ApplicationDbContext() { } public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Product> Products { get; set; } }
IProductRepo接口
public interface IProductRepo { Task<List<Product>> GetAll(); }
ProductRepo实现类
public class ProductRepo : IProductRepo { private readonly ApplicationDbContext _context; public ProductRepo() { this._context = new ApplicationDbContext(); } public async Task<List<Product>> GetAll() { return await this._context.Products.ToListAsync(); } }
尝试的加载方法
public void LoadData() { List<Data.Models.Product> ProductFromDB = this._repo.GetAll().Result; foreach (var p in ProductFromDB) { Products.Add(p); } }
问题原因
代码中使用_repo.GetAll().Result阻塞等待异步任务完成,会导致UI线程死锁:UI线程等待异步任务结束,而异步任务执行完成后默认会尝试回到原UI线程继续处理,但此时UI线程已被阻塞,任务无法完成,最终数据无法加载,界面无显示。
解决方法
1. 重构ViewModel异步加载逻辑
利用Prism的INavigationAware接口的异步OnNavigatedTo方法(Prism支持返回Task的异步版本),避免在构造函数中阻塞UI线程:
public class ProductViewModel : BindableBase, INavigationAware { private readonly IProductRepo _repo; private ObservableCollection<Data.Models.Product> _products; public ObservableCollection<Data.Models.Product> Products { get => _products; set => SetProperty(ref _products, value); } // 推荐通过依赖注入获取仓储实例,而非直接new public ProductViewModel(IProductRepo repo) { _repo = repo; // 先初始化空集合,避免DataGrid绑定为空 Products = new ObservableCollection<Data.Models.Product>(); } public async void OnNavigatedTo(NavigationContext navigationContext) { // 异步获取数据,全程使用await var products = await _repo.GetAll(); // 更新集合:清空现有数据并添加新数据 Products.Clear(); foreach (var product in products) { Products.Add(product); } // 也可以直接替换整个集合:Products = new ObservableCollection<Data.Models.Product>(products); } public bool IsNavigationTarget(NavigationContext navigationContext) => true; public void OnNavigatedFrom(NavigationContext navigationContext) { } }
2. 移除阻塞式异步调用
删除所有使用.Result或.Wait()的代码,全程用await等待异步操作,这是避免死锁的核心。
3. 优化DbContext的依赖注入(推荐)
不要在ProductRepo中直接new ApplicationDbContext,通过依赖注入管理DbContext生命周期,避免资源泄漏:
public class ProductRepo : IProductRepo { private readonly ApplicationDbContext _context; public ProductRepo(ApplicationDbContext context) { _context = context; } public async Task<List<Product>> GetAll() { // 使用ConfigureAwait(false)避免强制回到UI线程,进一步降低死锁风险 return await _context.Products.ToListAsync().ConfigureAwait(false); } }
在Prism容器中注册DbContext和仓储(例如在App.xaml.cs的RegisterTypes方法):
protected override void RegisterTypes(IContainerRegistry containerRegistry) { // 注册SQL Server DbContext containerRegistry.RegisterSingleton<ApplicationDbContext>(() => new ApplicationDbContext(new DbContextOptionsBuilder<ApplicationDbContext>() .UseSqlServer("你的SQL Server连接字符串") .Options)); // 注册仓储接口与实现 containerRegistry.Register<IProductRepo, ProductRepo>(); // 注册ViewModel等其他服务... }
关键注意点
- 全程异步:从仓储层到ViewModel,所有异步操作必须用
await,禁止阻塞线程。 - 导航生命周期:Prism的
OnNavigatedTo是ViewModel初始化后的合适时机,适合异步加载数据,不会阻塞UI。 - 属性通知:如果替换整个
Products集合,需通过BindableBase的SetProperty触发PropertyChanged;如果仅修改集合内元素,ObservableCollection会自动触发集合变化通知。
内容的提问来源于stack exchange,提问作者Maurice Filiatreault Hebert
相关产品推荐
相关产品推荐

