Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM How to set datacontext when viewmodel uses async

After hours of searching I am still without answer to this question. I have read this nice writing about async MVVM and made my viewmodel to use factory method.

public class MainViewModel
{
    // sic - public, contrary to the pattern in the article I cite
    // so I can create it in the Xaml as below
    public MainViewModel() 
    {
    }

    private async Task InitializeAsync()
    {
        await DoSomethingAsync();
    }

    public static async Task<MainViewModel> CreateAsync()
    {
        var ret = new MainViewModel();
        await ret.InitializeAsync();
        return ret;
    }
}

This is clear for me, but I can't understand how to make instance of MainViewModel and set it to datacontext in MainPage. I can't simply write

<Page.DataContext>
    <viewModel:MainViewModel/>
</Page.DataContext>

because I should use MainViewModel.CreateAsync()-method. And I can't do it on code-behind, which I even want to do, because code-behind -constructor is normal method, not an async-method. So which is proper way to continue?

like image 619
Paulus Limma Avatar asked Jun 06 '15 10:06

Paulus Limma


2 Answers

made my viewmodel to use factory method

I'm normally a fan of that approach - it's my favorite way to work around the "no async constructors" limitation. However, it doesn't work well in the MVVM pattern.

This is because VMs are your UI, logically speaking. And when a user navigates to a screen in an app, the app needs to respond immediately (synchronously). It doesn't necessarily have to display anything useful, but it does need to display something. For this reason, VM construction must be synchronous.

So, instead of trying to asynchronously construct your VM, first decide what you want your "loading" or "incomplete" UI to look like. Your (synchronous) VM constructor should initialize to that state, and it can kick off some asynchronous work that updates the VM when it completes.

This is not too hard to do by hand, or you can use the NotifyTaskCompletion approach that I described in an MSDN article on async MVVM data binding to drive the state transition using data bindings.

like image 102
Stephen Cleary Avatar answered Sep 22 '22 03:09

Stephen Cleary


You have to initalize the viewmodel before the window is open. Go to your App.xaml file and remove the part: StartupUri="MainWindow.xaml". Then you go to the App.xaml.cs and add this:

protected async override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var mainWindow = new MainWindow { DataContext = await CreateAsync() };
    mainWindow.Show();
}
like image 45
Snicker Avatar answered Sep 23 '22 03:09

Snicker