WPF MVVM Loading data on startup

mvvmwpf

I am building a simple master-details GUI using WPF/MVVM/Prism for some data coming from a Restful service.

I have the main window as my main view with a corresponding view model.

I have a user control to show the master data and it's got its own view model as well.

I have added an instance of the master data view model into the main view model. I am not sure if this is the best practice. Then I've set the data context of the main view in the code behind, and of the user control using the XAML of the main view.

<Window x:Class="MyNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyNamespace"
        xmlns:views="clr-namespace:MyNamespace.Views"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoginAndLoadMasterDataCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <views:MasterDataView DataContext="{Binding MasterData, Mode=OneWay}" />
    </Grid>
</Window>

Now I got two commands one to login to the restful and this is in the main view model, and another to load the master data and this is in the child view model. Loading data might take few seconds.

I want to fire the two commands in sequence, and I've tried Prism composite command and also tried to add both commands to be triggered on view load but both way don't enable me to control the sequence of commands so loading data is attempted before login returns which causes an exception to be thrown.

internal class MainViewModel : BindableBase
{
    private readonly ApiClient client = new ApiClient();

    public MasterDataViewModel MasterData { get; }

    public ICommand LoginAndLoadMasterDataCommand { get; }

    public  MainViewModel()
    {
        MasterData = new MasterDataViewModel(client);

        CompositeCommand loginCommand = new CompositeCommand(false);
        loginCommand.RegisterCommand(new DelegateCommand(async () => await client.Login()));
        loginCommand.RegisterCommand(MasterData.LoadDataCommand);

        LoginCommand = loginCommand;
    }
}

Notice that the login method and the load data method are both async methods.

What is a good practice to handle such a situation?

And also what if I wanted to set the window to a certain state (e.g. greyed out or showing a 'wait' modal) until the loading finishes?

Best Answer

I would create one single DelegateCommand to call Login and LoadData in such manner that I have control over the order they are called:

LoginAndLoadMasterDataCommand = new DelegateCommand(
    async () =>
    {
        IsLoading = true;

        bool loginSuccesful = await client.Login();
        if (loginSuccesful)
            await MasterData.LoadData();

        IsLoading = false;
    }

IsLoading is an observable property that you can bind to some ProgressBar or other control's Visibility to provide feedback to the user that the application is working. And MasterData.LoadData is, supposedly, the awaitable method that your MasterData.LoadDataCommand is calling.

Related Topic