繁体   English   中英

在视图 model 构造函数中异步初始化后 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.

 
粤ICP备18138465号  © 2020-2023 STACKOOM.COM