Data binding WinRT ViewModel数据绑定到异步方法

Data binding WinRT ViewModel数据绑定到异步方法,data-binding,mvvm,asynchronous,viewmodel,windows-runtime,Data Binding,Mvvm,Asynchronous,Viewmodel,Windows Runtime,我正在对XML文件中的对象列表进行反序列化,并希望通过ViewModel绑定到视图中这些对象的实际内容。问题是文件操作是异步的,这会一直冒泡到ViewModel,在ViewModel中属性getter不能被标记为异步的 问题 我将文件夹中的所有XML文件反序列化为Profile对象,并将它们存储在列表中。此方法(必须)标记为async public static async Task<List<Profile>> GetAllProfiles() {

我正在对XML文件中的对象列表进行反序列化,并希望通过ViewModel绑定到视图中这些对象的实际内容。问题是文件操作是异步的,这会一直冒泡到ViewModel,在ViewModel中属性getter不能被标记为异步的

问题
  • 我将文件夹中的所有XML文件反序列化为
    Profile
    对象,并将它们存储在
    列表中。此方法(必须)标记为
    async

        public static async Task<List<Profile>> GetAllProfiles()
        {
            DataContractSerializer ser = new DataContractSerializer(typeof(Profile));
            StorageFolder folder = await ApplicationData.Current.RoamingFolder.CreateFolderAsync("Profiles", CreationCollisionOption.OpenIfExists);
    
            List<Profile> profiles = new List<Profile>();
            foreach (var f in await folder.GetFilesAsync())
            {
                var fs = await f.OpenStreamForReadAsync();
                profiles.Add((Profile)ser.ReadObject(fs));
               fs.Dispose();
            }
    
            return profiles;
        }
    
    • 但这永远不会执行(由于某种原因,它会阻塞
      await文件夹.getfileasync()
      调用)
    当前解决方案 调用一个
    async
    Initialize()方法,该方法在变量中加载
    GetProfiles()
    函数的结果,然后进行
    NotifyPropertyChanged(“列表”)
    调用:

        public ViewModel()
        {
            Initialize();
        }
    
        public async void Initialize()
        {
            _profiles = await Profile.GetAllProfiles();
            NotifyPropertyChanged("Lists");
        }
    
        private List<Profile> _profiles;
        public ObservableCollection<string> Lists
        {
            get
            {
                if (_profiles != null)
                    return new ObservableCollection<string>(_profiles.Select(p => p.Name));
                else
                    return null;
            }
        }
    
    公共视图模型()
    {
    初始化();
    }
    公共异步void Initialize()
    {
    _profiles=等待Profile.GetAllProfiles();
    NotifyPropertyChanged(“列表”);
    }
    私人名单(个人资料),;
    公共可观测收集列表
    {
    得到
    {
    如果(_profiles!=null)
    返回新的observeCollection(_profiles.Select(p=>p.Name));
    其他的
    返回null;
    }
    }
    
    问题: 有更好的办法吗? 是否有我尚未发现的模式/方法

    编辑 在执行非UI代码时会出现问题的根源,您不能依赖NotifyPropertyChanged来执行某些线程同步工作。-必须等待方法初始化,并且CTOR不能是异步的,所以本质上这是一种无用的模式

        public MyClass()
        {
            Initialize();
        }
    
        public async void Initialize()
        {
            _profiles = await Profile.GetAllProfiles();
        }
    
        private ObservableCollection<Profile> _profiles;
        public ObservableCollection<string> Lists
        {
            get
            {
                return _profiles; // this will always be null
            }
        }
    
    publicmyclass()
    {
    初始化();
    }
    公共异步void Initialize()
    {
    _profiles=等待Profile.GetAllProfiles();
    }
    私有可观察收集_配置文件;
    公共可观测收集列表
    {
    得到
    {
    return _profiles;//这将始终为空
    }
    }
    
    属性不能是异步的,因此此解决方案不会像您提到的那样工作。Result等待任务完成,但这会阻塞I/O操作的异步回调返回的UI线程,因此您的应用程序处于死锁状态,因为从未调用回调。你的解决方案确实是最好的方法。不过,它还可以改进

    • 您应该将_profiles字段设置为ObservableCollection,这样就不需要每次访问列表时都将列表转换为OC
    • 由于您执行的I/O操作可能需要任意的时间,因此您应该在执行过程中启用某种进度指示器
    • 在某些情况下,您可能希望Lists属性更懒惰,并且只在第一次访问Init方法时调用它

    感谢您的回答和提示。我实现了它们,真的更好!遗憾的是,没有更干净的方法来做这件事。。。有什么线索可以解释为什么理想的解决方案2会阻塞(并且永远不会恢复)?Result属性不是异步的(正如我们确定的),而是一个同步调用,因此线程的执行会暂停,直到返回结果,这在任务完成时发生。我的理解是异步I/O操作在同一个线程上返回某种事件,但是该线程的调度程序没有运行,因为该线程暂停等待Result属性返回,因此无法处理任何事件,结果是。设置_profiles值时,应引发PropertyChanged事件。此外-"配置文件和列表是不同的类型-它们需要是相同的类型。同意(这只是复制/粘贴伪代码),但这只是普通的C",因此没有可用的UI代码或PropertyChanged事件。我想要的是从一个异步方法获得结果,而不必将我的所有方法都标记为异步(因为当您点击UI时,它会停止),并且.Result()调用死锁,正如我们在下面发现的那样……那么,您需要在视图模型中使用它,实现INotifyPropertyChanged并引发事件。假设其代码用于与UI无关的业务类:)那么它将位于MVVM世界的模型端,视图模型需要等待模型更新后再引发其PropertyChanged。否则,如果您的视图绑定到您的业务逻辑模型,则不应该有任何东西阻止您在业务逻辑模型中实现INotifyPropertyChanged。如果您不能这样做,您需要等待配置文件列表加载。
        public ViewModel()
        {
            Initialize();
        }
    
        public async void Initialize()
        {
            _profiles = await Profile.GetAllProfiles();
            NotifyPropertyChanged("Lists");
        }
    
        private List<Profile> _profiles;
        public ObservableCollection<string> Lists
        {
            get
            {
                if (_profiles != null)
                    return new ObservableCollection<string>(_profiles.Select(p => p.Name));
                else
                    return null;
            }
        }
    
        public MyClass()
        {
            Initialize();
        }
    
        public async void Initialize()
        {
            _profiles = await Profile.GetAllProfiles();
        }
    
        private ObservableCollection<Profile> _profiles;
        public ObservableCollection<string> Lists
        {
            get
            {
                return _profiles; // this will always be null
            }
        }