WinRT ViewModel DataBind为异步方法 [英] WinRT ViewModel DataBind to async method

查看:138
本文介绍了WinRT ViewModel DataBind为异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在反序列化XML文件中的对象列表,并希望绑定到View中的这些对象的实际内容,并传递一个ViewModel。问题是文件操作是 async ,并且这一直到ViewModel,其中Property getter不能被标记为...



问题




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

      public static async任务<列表< Profile>> GetAllProfiles()
    {
    DataContractSerializer ser = new DataContractSerializer(typeof(Profile));
    StorageFolder文件夹= await ApplicationData.Current.RoamingFolder.CreateFolderAsync(Profiles,CreationCollisionOption.OpenIfExists);

    列表<个人资料> profiles = new List< Profile>();
    foreach(var f in await folder.GetFilesAsync())
    {
    var fs = await f.OpenStreamForReadAsync();
    profiles.Add((Profile)ser.ReadObject(fs));
    fs.Dispose();
    }

    返回配置文件;
    }




理想解决方案1 ​​




  • 我的ViewModel中的binding属性然后将 调用这样的静态方法

      public async任务< ObservableCollection< string>>列出
    {
    get
    {
    返回新的ObservableCollection< string>(GetAllProfiles())Select(p => p.Name));
    }
    }


  • 但是无法将属性标记为 async




理想解决方案2



  public ObservableCollection< string>列出
{
get
{
返回新的ObservableCollection< string>((GetAllProfiles()。Result).Select(p => p.Name));
}
}




  • 但这不会执行(它阻止在 await folder.GetFilesAsync()调用某些原因)



< h2>当前解决方案

调用 async 加载 GetProfiles()函数,然后使一个 NotifyPropertyChanged(Lists)调用:

  public ViewModel()
{
Initialize();
}

public async void Initialize()
{
_profiles = await Profile.GetAllProfiles();
NotifyPropertyChanged(Lists);
}

私人列表<个人资料> _profiles;
public ObservableCollection< string>列出
{
get
{
if(_profiles!= null)
返回新的ObservableCollection< string>(_ profiles.Select(p => p.Name) );
else
返回null;
}
}



问题



有更好的方法吗?
有没有一个我还没有发现的模式/方法?



编辑



在执行非UI代码时出现问题的根源,您不能依赖NotifyPropertyChanged来执行一些线程同步的东西。 - 方法Initialize必须等待,并且ctors不能异步,所以这是模式是无用的。

  public MyClass )
{
Initialize();
}

public async void Initialize()
{
_profiles = await Profile.GetAllProfiles();
}

private ObservableCollection<个人资料> _profiles;
public ObservableCollection< string>列出
{
get
{
return _profiles; //这将永远是空的
}
}


解决方案

属性不能异步,因此此解决方案将无法正常工作。 Task.Result等待完成任务,但这是阻止您的UI线程,其中I / O操作的异步回调返回,因此您的应用程序死锁,因为从未调用回调。你的解决方案真的是最好的方法。可以改进。




  • 您应该将_profiles字段设置为ObservableCollection,因此您每次访问列表时都不需要将列表转换为OC。

  • 由于您正在执行可能需要花费大量时间的I / O操作 - 您应该在进行过程中启用某种进度指示器。

  • 在某些情况下,您可能希望Lists属性变得懒惰,并且只能在第一次访问时调用Init方法。


I am deserializing a list of objects from an XML file, and would like to bind to the actual content of those objects in my View, passing over a ViewModel. The problem is that file operations are async and this bubbles all the way up to the ViewModel, where Property getters cannot be marked as such...

Problem

  • I deserialize all XML files in a folder to Profile objects and store them in a List<Profile>. This method (has to be) marked 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;
        }
    

Ideal solution 1

  • The binding property in my ViewModel would then ideally call that static method like this

    public async Task<ObservableCollection<string>> Lists
    {
        get 
        {
            return new ObservableCollection<string>(GetAllProfiles().Select(p => p.Name));
        }
    }
    

  • BUT Properties cannot be marked async

Ideal solution 2

    public ObservableCollection<string> Lists
    {
        get 
        {
            return new ObservableCollection<string>((GetAllProfiles().Result).Select(p => p.Name));
        }
    }

  • BUT this never executes (it blocks in the await folder.GetFilesAsync() call for some reason)

Current solution

Calls an async Initialize() method that loads the result of the GetProfiles() function in a variable, and then makes a NotifyPropertyChanged("Lists") call:

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

Question

Is there a better way? Is there a pattern/method that I haven't yet discovered?

Edit

The root of the problem appears when doing non-UI code, and you cannot rely on the NotifyPropertyChanged to do some thread-synchronization stuff. -- The method Initialize has to be awaited and ctors cannot be async, so essentialy this is pattern is useless.

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

解决方案

Properties can't be async so this solution will not work as you mentioned. Task.Result waits for the task to complete, but this is blocking your UI thread where the I/O operation's async callback returns, so you are deadlocking your application, since the callback is never called. Your solution really is the best way. It could be improved though.

  • You should make the _profiles field an ObservableCollection, so you would not need to convert the List to the OC every time the list is accessed.
  • Since you are performing an I/O operation that can take arbitrary amount of time - you should enable some sort of a progress indicator while it is in progress.
  • In some cases you might want the Lists property to be lazier and only call the Init method the first time it is accessed.

这篇关于WinRT ViewModel DataBind为异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆