
[英]cell remains in edit mode even after it loses focus in wpf datagrid
[英]Datagrid remains empty after asynchronous initialization in view model constructor
我有一个 WPF 应用程序,其视图包含数据网格,视图 model 具有通过在构造函数中调用异步方法初始化的可观察集合。 但是在运行代码时数据网格仍然是空的。
视图 model class 看起来像这样。
internal class MainWindowViewModel : INotifyPropertyChanged
{
private readonly IBookingRecordService service;
public event PropertyChangedEventHandler? PropertyChanged;
private ObservableCollection<BookingRecord> bookingRecords = new();
public ObservableCollection<BookingRecord> BookingRecords
{
get => bookingRecords;
set
{
bookingRecords = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingRecords)));
}
}
public MainWindowViewModel()
{
service = new BookingRecordService();
Task.Run(() => LoadBookingRecords());
}
private async Task LoadBookingRecords()
{
BookingRecords = new ObservableCollection<BookingRecord>(await service.Get());
}
}
我在构造函数中全部使用 LoadBookingRecords(),以便数据在视图 model 初始化时开始加载,但我是异步执行的,因此它不会阻塞 UI 线程并使应用程序无响应。
我尝试通过等待构造函数中任务的完成
Task.Run(() => LoadBookingRecords()).Wait();
检查这是否与异步 function 调用有关。 事实上,如果我等待方法在构造函数中完成,数据网格就会正确显示。 但我不想等待任务在 UI 线程上完成,因为它会阻塞 UI。
我读到必须在 UI 线程上引发 PropertyChanged 事件才能触发 UI 更新,我想这就是问题所在。 我也读过可以使用
Application.Current.Dispatcher.BeginInvoke()
安排委托尽快在 UI 线程上运行,所以我尝试了以下操作。
private async Task LoadBookingRecords()
{
await Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
{
BookingRecords = new ObservableCollection<BookingRecord>(await service.Get());
}));
}
但这也使 DataGrid 为空。
“'异步......在构造函数中”是你必须避免的事情。
必须等待异步调用,这不能在构造函数中完成。 改为声明可等待的 Initialize 方法
public Task Initialize()
{
return LoadBookingRecords();
}
并在 MainWindow 的异步 Loaded 事件处理程序中调用它:
private static async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
await viewModel.Initialize();
}
或者,创建一个工厂方法,如
public static async Task<MainWindowViewModel> Create()
{
var viewModel = new MainWindowViewModel();
await viewModel.LoadBookingRecords();
return viewModel;
}
并在 Loaded 处理程序中调用它:
private static async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
DataContext = await MainWindowViewModel.Create();
}
基于 Clemens 的回答,我尝试了一些不同的东西,以避免触及 MainWindow 代码隐藏。
我在构造函数中删除了对 LoadBookingRecords 的调用,而是创建了一个委托命令作为保存此方法的属性。
internal class MainWindowViewModel : INotifyPropertyChanged
{
private readonly IBookingRecordService service;
private ObservableCollection<BookingRecord> bookingRecords = new();
public ICommand LoadBookingRecordsCommand { get; set; }
public ObservableCollection<BookingRecord> BookingRecords
{
get => bookingRecords;
set
{
bookingRecords = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingRecords)));
}
}
public MainWindowViewModel()
{
service = new BookingRecordService();
LoadBookingRecordsCommand = new DelegateCommand(async _ => await LoadBookingRecords());
}
private async Task LoadBookingRecords()
{
BookingRecords = new ObservableCollection<BookingRecord>(await service.Get());
}
}
然后我将 NuGet package Microsoft.Xaml.Behaviors.Wpf 添加到项目并将以下命名空间添加到 MainWindow XAML。
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
最后,我将委托命令绑定到 MainWindow 的 Loaded 事件。
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadBookingRecordsCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
现在数据网格在加载后正确显示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.