data-binding - WinRT ViewModel DataBind 到异步方法

标签 data-binding mvvm asynchronous viewmodel windows-runtime

我正在反序列化 XML 文件中的对象列表,并希望通过 ViewModel 绑定(bind)到我的 View 中这些对象的实际内容。问题是文件操作是async这会一直冒泡到 ViewModel,其中属性 getter 不能被标记为这样......

问题

  • 我将文件夹中的所有 XML 文件反序列化为 Profile对象并将它们存储在 List<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;
        }
    

  • 理想解决方案 1
  • 然后,我的 ViewModel 中的绑定(bind)属性将理想地调用该静态方法,如下所示
    public async Task<ObservableCollection<string>> Lists
    {
        get 
        {
            return new ObservableCollection<string>(GetAllProfiles().Select(p => p.Name));
        }
    }
    
  • 但是无法标记属性 async

  • 理想方案2
        public ObservableCollection<string> Lists
        {
            get 
            {
                return new ObservableCollection<string>((GetAllProfiles().Result).Select(p => p.Name));
            }
        }
    
  • 但是这永远不会执行(由于某种原因它在 await folder.GetFilesAsync() 调用中阻塞)

  • 当前解决方案

    调用async Initialize() 方法加载 GetProfiles() 的结果变量中的函数,然后生成 NotifyPropertyChanged("Lists")称呼:
        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;
            }
        }
    

    问题

    有没有更好的办法?
    有没有我还没有发现的模式/方法?

    编辑

    问题的根源出现在做非 UI 代码时,你不能依赖 NotifyPropertyChanged 来做一些线程同步的事情。 -- Initialize 方法必须等待,并且ctors 不能是异步的,所以本质上这是模式是无用的。
        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
            }
        }
    

    最佳答案

    属性不能是异步的,所以这个解决方案不会像你提到的那样工作。 Task.Result 等待任务完成,但这会阻塞 I/O 操作的异步回调返回的 UI 线程,因此您将死锁您的应用程序,因为从不调用回调。您的解决方案确实是最好的方法。不过可以改进。

  • 您应该将 _profiles 字段设置为 ObservableCollection,这样您就不需要在每次访问列表时都将列表转换为 OC。
  • 由于您正在执行可能需要任意时间的 I/O 操作 - 您应该在它进行时启用某种进度指示器。
  • 在某些情况下,您可能希望 Lists 属性更加惰性,并且仅在第一次访问时调用 Init 方法。
  • 关于data-binding - WinRT ViewModel DataBind 到异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11849213/

    相关文章:

    wpf - 如何使 WPF TreeView 数据绑定(bind)惰性和异步?

    c# - 从 WP8.1 重定向到 WP8 后,ItemsControl 的数据绑定(bind)不起作用

    android - View :null 上的 View 标记不正确

    c# - MVVM:强制组合框标签刷新

    JavaScript : Async/Await with a loop inside

    c# - 通过旋转子列表将具有子列表的自定义对象 (List<T>) 绑定(bind)到网格

    wpf - ViewModel 位置到子文件夹(XAML,命名空间)

    wpf - MVVM设计模式

    javascript - 当存在 constructor() 时调用异步函数

    java - Java 应用服务器中的 CompletableFuture/parallel Stream