【发布时间】:2012-08-04 15:49:54
【问题描述】:
我正在反序列化 XML 文件中的对象列表,并希望通过 ViewModel 绑定到我的视图中这些对象的实际内容。问题是文件操作是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; }
理想方案一
-
我的 ViewModel 中的绑定属性会理想地像这样调用静态方法
public async Task<ObservableCollection<string>> Lists { get { return new ObservableCollection<string>(GetAllProfiles().Select(p => p.Name)); } } BUT 属性无法标记
async
理想方案2
public ObservableCollection<string> Lists
{
get
{
return new ObservableCollection<string>((GetAllProfiles().Result).Select(p => p.Name));
}
}
- 但是这永远不会执行(由于某种原因它在
await folder.GetFilesAsync()调用中阻塞)
当前解决方案
调用asyncInitialize() 方法,将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
}
}
【问题讨论】:
-
您应该在设置 _profiles 值时引发 PropertyChanged 事件。此外 - _profiles 和 Lists 是不同的类型 - 它们需要是相同的类型。
-
同意(这只是复制/粘贴伪代码),但这只是普通的 C#,因此没有可用的 UI 代码和 PropertyChanged 事件。我想要的是从异步方法中获取结果,而不必将我的所有方法都标记为异步(因为当您点击 UI 时,它会停止),并且 .Result() 调用死锁,正如我们在下面发现的......
-
那么,您需要在视图模型中使用它,实现 INotifyPropertyChanged 并引发事件。
-
假设它的代码用于与 UI 无关的业务类 :)
-
那么它将位于 MVVM 世界的模型端,并且视图模型需要等待模型更新,然后再提升其 PropertyChanged。否则,如果您的视图绑定到您的业务逻辑模型 - 应该没有什么可以阻止您在业务逻辑模型中实现 INotifyPropertyChanged。如果你不能这样做 - 你需要等待配置文件列表加载。
标签: data-binding mvvm asynchronous viewmodel windows-runtime