WinRT ViewModel DataBind为异步方法 [英] WinRT ViewModel DataBind to async method
问题描述
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 aList<Profile>
. This method (has to be) markedasync
.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屋!