Search code examples
c#.netmvvmwindows-store-apps

MVVM viewmodel async data initialization


I am trying to get familiar with Windows Store apps and the MVVM pattern (I'm new to this platform). I have a very simple app that parses a list of Person objects from a json file, then shows them in a gridView. I have created a PeopleViewModel class to serve as the view model for my mainpage, which handles the parsing, and exposes the array of objects for the view. The parsing method:

public async Task init()
{
    StorageFolder resourceFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
    resourceFolder = await resourceFolder.GetFolderAsync("Resources");

    StorageFile resourceFile = await resourceFolder.GetFileAsync("persons.json");
    string fileContent = await Windows.Storage.FileIO.ReadTextAsync(resourceFile);

    ObservableCollection<Person> persons = new ObservableCollection<Person>();

    JsonValue json = JsonValue.Parse(fileContent);
    int personCount = json.GetArray().Count;
    for (int i = 0; i < personCount; i++)
    {
        IJsonValue element = json.GetArray()[i];
        Person p = new Person(element);
        persons.Add(p);
    }

    _persons = persons;
}

Then in my XAML, I set this class as the data context of the page:

<!-- viewModel namespace defined above -->
<Page.DataContext>
    <viewModel:PeopleViewModel/>
</Page.DataContext>

Since reading a file is an async operation, I can't put it in the default constructor of PeopleViewModel, I have to call its init() method from the code-behind file of my xaml:

private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    PeopleViewModel viewModel = this.DataContext as PeopleViewModel;
    await viewModel.init();
}

My code is working well, but I'm wondering if this is the right way to do it. Is there a way to initialize my viewmodel with an async method, and keep my code-behind file "clean" (or is this solution considered clean)?


Solution

  • Sure there is, just call an async void method from the constructor (no await needed), which can in turn now call methods with await. (And yes, the best practice is to keep the codebehind clean.)

    EDIT:

    So based on your experience I left out the part saying that you shouldn't really do this. Instead set up something where an event notifies your viewmodel to load things. Basically you should only use async void with event handlers. More on this at the bottom of this page: http://caraulean.com/blog/2013/07/15/using-caliburn-micro-with-async-await/ (Although I prefer MVVMlight or PRISM - the latter even gives you the INavigationAware interface where you get your OnNavigatedTo events in you VM as well.)