WinRT ViewModel DataBind到异步方法

时间:2012-08-07 15:29:19

标签: data-binding mvvm asynchronous viewmodel windows-runtime

我正在从XML文件反序列化对象列表,并希望绑定到View中这些对象的实际内容,并通过ViewModel传递。问题是文件操作是async,这一直到ViewModel,其中Property 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中的绑定属性理想地调用这样的静态方法

    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
        }
    }

1 个答案:

答案 0 :(得分:1)

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

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