Reputation: 668
I have a sequencing problem in my ViewModel, which seems to be thread-related but I can't quite figure our what's going wrong or how my "fix" is fixing it.
I have a ViewModel that needs to call an async method to load its initial data. I am calling the async method from the Init method which MvvmCross calls automatically. If the loading fails for any reason I want to show an Error screen, but calling ShowViewModel inside the method called by Init does not produce the expected result. ShowViewModel is called correctly, but from following this in the debugger it appears that the ErrorViewModel shows before the original ViewModel / View has finished loading - so it loads but doesn't appear, as it is over-written by the original ViewModel.
Here's a simplified version of the loading code:
public async Task Init()
{
await LoadInitialDataAsync();
}
protected async Task LoadInitialDataAsync()
{
var loadResult = await LoadSomeStuffAsync();
if (loadResult.IsBadNews)
{
ShowViewModel<ErrorViewModel>();
return;
}
}
The MvxTrace logs tell me that ShowViewModel is called on ErrorViewModel immediately after it is called on the initial ViewModel, but only the initial View shows, not the ErrorView.
To "fix" this I can do one of two things.
I can wrap the call to LoadInitialDataAsync in Task.Run:
await Task.Run(async () =>
{
await LoadInitialDataAsync();
});
Or, I can add a small delay before the inner ShowViewModel call:
protected async Task LoadInitialDataAsync()
{
var loadResult = await LoadSomeStuffAsync();
if (loadResult.IsBadNews)
{
await Task.Delay(1);
ShowViewModel<ErrorViewModel>();
return;
}
}
Either of these changes produces the desired result - if a Bad Thing happens during load, the ErrorViewModel shows its view.
The problem is, I don't trust this fix because I don't understand what's going wrong under the proverbial hood, and therefore don't know how robust this fix is. It seems like an arbitrary timing thing which will break again at some point in the future at the most inconvenient time.
If anyone understands the MvvmCross internals well enough to help with this, I'd appreciate it!
Upvotes: 3
Views: 1309
Reputation: 3888
The Init
method should do very little. Init
is typically used to copy some navigation parameters passed into ShowViewModel<TViewModel>()
. The Start
method is where you are intended to do ViewModel startup such as calling LoadInitialDataAsync
. Please review the App Lifecycle documentation for more information.
Upvotes: 3
Reputation: 643
Based on https://github.com/MvvmCross/MvvmCross/wiki/viewmodel--to-viewmodel-navigation:
When your app is displaying a ViewModel page, say FirstViewModel, then that first page can request that the display is moved forwards to a new ViewModel page, say SecondViewModel by using a call like:
ShowViewModel<SecondViewModel>();
When the FirstViewModel makes this request, then the MvvmCross framework will:
- Locate a View to use as a 'page' for SecondViewModel within the app - normally this will be SecondView
- Create a new instance of this SecondView
- Create a SecondViewModel and provide it as the DataContext for the new SecondView
- Ask the operating system to display the SecondView
The ShowViewModel<ErrorViewModel()
for you gets called, while the initial View hasnt shown yet, which might be the issue for it and would fit to your debugger behaviour.
The delay gives it probably enough time to show and the async task doesnt block it, so it shows, then the showing is finished and it switches over to the other view.
One option would probably to do the initialization before and pass the data with the ShowViewModel, though i dont have experience with that. Or save the error state, let the view load and call the viewmodel from the view to let it switch after creating.
Though iam not really fan of either of those, thats why i do my initialization via services before and if it fails react before switching to the new viewmodel.
Dunno how complex your current structure is and what would work out best.
Upvotes: 1