Search code examples
asynchronousmvvmwindows-runtimeasync-awaitmvvm-light

How I can safely run async code, when handling messages from Messenger?


I am using MVVM, inparticular MVVMLight. For boradcasting to all of my modelviews, that no internet connection is available I am using Messenger class. The modelviews subscribe to this event in order to reload itself with offline data, inform user etc.

However, I have a problem. When I have the folowing handler:

private void HandleNoInternetMessage(NoInternetAccessMessage obj)
{
    Task.Run(async () => await InitializeForOfflineInternalAsync());
}
public async Task InitializeForOfflineInternalAsync()
{
    try
    {
        WaitingLayerViewModel.ShouldBeVisible = true;
        WaitingLayerViewModel.IsBusy = true; //<--exception HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)
        bool switchToOffline = await CommonViewModelProvider.InformUserOfNoInternetAccessAndChangeAppState(); //<!- CoreWindow.GetForCurrentThread().Dispatcher is null
        await FilterTestItemViewModel.InitializeForOfflineAsync();
        await FilterTestItemViewModel.InitializeForOfflineAsync();
        WaitingLayerViewModel.ShouldBeVisible = false;
        WaitingLayerViewModel.IsBusy = false;
        ...
    }
}

I got exception HRESULT: 0x8001010E (RPC_E_WRONG_THREAD), because in InitializeForOfflineInternalAsync I am changing some properties of the viewmodel wchich are bound in XAML (or at least I think it is because of that). However, it is weird, because I am changing in other code bound properties regularly and have no problems with it (and the thread is a working thread).

Now, how can i solve that?

  • The messanger let me provide only delegate which is not async (which make kind of sense), so I can not have the HandleNoInternetMessage method async

  • I am using async await ... no explicit spawning of threads

  • I dont have access in VM to Dispatcher, because I am in VM which should not know about platform dependent stuff. And when I tried to use it to show a message, NullPointer excpetion was thrown when calling CoreWindow.GetForCurrentThread().Dispatcher; And again when calling from other places, no such exception was thrown

I guess the question is How I can safely run async code, which changes boudn properties, when handling messages from Messenger?


Solution

  • You're responding to messages that are logically events, so this is an acceptable use case for async void.

    private async void HandleNoInternetMessage(NoInternetAccessMessage obj)
    {
        await InitializeForOfflineInternalAsync();
    }
    
    public async Task InitializeForOfflineInternalAsync()
    {
        try
        {
            WaitingLayerViewModel.ShouldBeVisible = true;
            WaitingLayerViewModel.IsBusy = true;
            bool switchToOffline = await CommonViewModelProvider.InformUserOfNoInternetAccessAndChangeAppState();
            await FilterTestItemViewModel.InitializeForOfflineAsync();
            await FilterTestItemViewModel.InitializeForOfflineAsync();
            WaitingLayerViewModel.ShouldBeVisible = false;
            WaitingLayerViewModel.IsBusy = false;
            ...
        }
    }
    

    Remember that Task.Run is for CPU-bound code (as I describe on my blog).