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