I’m working on a mvvm light toolkit based project. I’ve got a MainView and a DetailsView with its
corresponding ViewModels. Both VMs are registered for a NotificationMessage.
// MainViewModel.cs and DetailsViewModel.cs
private void RegisterMessages()
{
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageHandler);
}
When a “ShowDetails” message is received, the MainViewModel calls a service that creates the ‘DetailsView’
// MainViewModel.cs
private void NotificationMessageHandler(NotificationMessage msg)
{
if (msg.Notification == "ShowDetails")
{
_detailsService.ShowDetails(); // Does something like (new DetailsView).ShowDialog()
}
}
The DetailsView uses the ViewModelLocator to get the existing DetailsViewModal as DataContext.
The DetailsViewModel should receive the “ShowDetails” message to update its internal state or request some data, too.
// DetailsViewModel.cs
private void NotificationMessageHandler(NotificationMessage msg)
{
if (msg.Notification == "ShowDetails")
{
UpdateViewModel();
}
}
Now the problem:
Because I want the DetailsView to be a modal window, I call ShowDialog() on it. This seems to block the messenger untill the DetailsView is closed again. So the DetailsViewModal receives the message after the modal window ist closed. Is there any solution to fix this?
I think it would work, if I could register the DetailsViewModal before the MainViewModel. This would change the order of the MessageHandler-calls and the VM update occures before the blocking ShowDialog(). But the MainViewModel is created and registered first due to it is what it is. The DetailsViewModel is created by the ViewModalLocator the first time it is needed, so it always loses the race.
Unfortunately, I was unable to reproduce your specific issue. I fired off a separate thread in my MainWindowView Loaded event handler; a thread which did nothing but continuously send a specific message. I then called ShowDialog() on my SecondWindowView, whose view model was registered to listen to this specific message. The message handler in the second window’s view model executed repeatedly. In fact, the handler was being called even before ShowDailog() was called since my view model was already created by the ViewModelLocator at application startup. I would need to see some more code to get a better idea of what’s going on in your case (i.e. your service that is creating the details window, or something that I can compile to reproduce the issue).
You might try the following approach, instead, for your child window. Define the following classes somewhere in your application:
Now create the following ChildWindowVM class and initialize it in your ViewModelLocator the same way the MainWindowVM is initialized:
The Refresh() method would be defined in the DetailsViewModel class and would take care of any initialization you would like to perform before displaying the window. Note that when the CurrentContent property is set then a message is fired off to the MainWindowView to create a ChildWindowView instance in which to display your content.
The MainWindowView code looks like this:
The last step is to bind the CurrentContent property from the ChildWindowVM class to your ChildWindowView class. This is done in the xaml for your ChildWindowView:
Now you can display your details from anywhere in your application simply by calling
And you can close the window programmatically by calling
You can also derive as many classes as you want from MessageBase and register for them in your ChildWindowVM class. In each message handler, you can then specify which content to show simply by setting the CurrentContent property to the appropriate view model.
One more thing, actually. You’ll need to specify the template binding between your views and view models if you actually want to see anything useful in your child window. This can be done via xaml in your application resources:
Don’t forget to define the namespaces (i.e. “viewmodels” and “views”).