运行 构造函数中的异步方法停止显示数据的 ObservableCollection

Running async method in constructor stops ObservableCollection showing data

我在构造函数中调用异步方法时遇到问题。它停止显示在 ObservableCollection 中的数据,即使我在跟踪代码时可以在调试中看到它。为了解决这个问题,我有一个相同方法的非异步版本,但显然这并不理想。如何让我的异步方法在构造函数中正常工作?

如果我在 MainWindowViewModel 的构造函数中调用 this.RefreshCarList(""),那么后续对 this.RefreshCarListAsync("") 的调用(不是从构造函数内部调用)将工作得很好并且数据会显示在屏幕上。如果我只调用 this.RefreshCarListAsync("") 那么数据永远不会显示。

public class MainWindowViewModel
{
    private ObservableCollection<Car> carList;
    public ICollectionView CarList { get; private set; }
    private DataReceivedHandler dataReceivedHandler = new DataReceivedHandler();

    public MainWindowViewModel()
    {
        //this.RefreshCarList(""); // Gets data on startup
        this.RefreshCarListAsync("") // Doesn't show any data
        this.CarList = new QueryableCollectionView(this.carList, typeof(Car));
    }

    public void RefreshCarList(string carBrand)
    {
        this.carList = this.dataReceivedHandler.GetCarList(carBrand);
    }

    public async Task RefreshCarListAsync(string carBrand)
    {
        this.carList = await this.dataReceivedHandler.GetCarListAsync(carBrand);
    }
}

视图模型使用从服务获取数据的数据接收器class:

public class DataReceivedHandler
{
    private CarDataService dataService = new CarDataService();
    private List<Car> carList = new List<Car>();
    private ObservableCollection<Car> carOC = new ObservableCollection<Car>();


    public ObservableCollection<Car> GetCarList(string carBrand)
    {
        carListFromService = this.dataService.GetCarList(carBrand);
        this.carOC.Clear();
        foreach (ICar car in carListFromService)
            this.carOC.Add(car);
        

        return (this.carOC);
    }

    public async Task<ObservableCollection<Car>> GetCarListAsync(string carBrand)
    {
        carListFromService = await Task.Run(() => this.dataService.GetCarList(carBrand)).ConfigureAwait(false);
        this.carOC.Clear();
        foreach (ICar car in carListFromService)
            this.carOC.Add(car);

        return (this.carOC);
    }
}

服务:

public class CarDataService
{
    private List<Car> CarList = new CarList();
    public List<Car> GetCarList(string carBrand)
    {
        return this.CarList;
    }
}

更新:

所以我试过这个作为建议的答案之一,但它仍然不起作用:

public async Task Initialise()
{
    try
    {
        await this.CarListAsync("");
        this.CarList = new QueryableCollectionView(this.carList, typeof(Car));
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

this.viewModel.Initialise(); // in a different class

注意this.carListRefreshCarListAsync中初始化:

this.carList = await this.dataReceivedHandler.GetCarListAsync(carBrand);

但它可能不会立即初始化,而是稍后在 GetCarListAsync 提供结果时初始化。然后在构造函数中你不等待 RefreshCarListAsync (无论如何你不能在构造函数中这样做):

this.RefreshCarListAsync("") // Doesn't show any data

因为你没有等待-下一个语句

this.CarList = new QueryableCollectionView(this.carList, typeof(Car));

执行(可能) this.carList 初始化之前。因此 this.carList 在该行执行时为空,很可能导致您的问题。

因此,如果您要从构造函数执行此操作,则需要执行以下操作:

private async Task Initialize() {
    try {
          await this.RefreshCarListAsync("") // Doesn't show any data
          this.CarList = new QueryableCollectionView(this.carList, typeof(Car));
    }
    catch (Exception ex) {
         // do something meaningful, otherwise it will go unnoticed
    }
}

并从构造函数中调用它。或者更好 - 让它成为 public 并且不要从构造函数调用它但让任何使用你的视图模型来调用它,因为它有可能等待它。

如果在构造函数返回后动态设置 属性,您应该实施 INotifyPropertyChanged 并在 MainWindowViewModel [=16= 中引发 PropertyChanged 事件]:

public class MainWindowViewModel : INotifyPropertyChanged
{
    private ICollectionView  carList;
    public ICollectionView  CarList
    {
        get { return carList; }
        private set { carList = value; NotifyPropertyChanged(); }
    }

    private DataReceivedHandler dataReceivedHandler = new DataReceivedHandler();

    public async Task Initialise()
    {
        try
        {
            await this.CarListAsync("");
            this.CarList = new QueryableCollectionView(this.carList, typeof(Car));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // This method is called by the Set accessor of each property.  
    // The CallerMemberName attribute that is applied to the optional propertyName  
    // parameter causes the property name of the caller to be substituted as an argument.  
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}