Let's imagine I have some user control. The user control has some child windows. And user control user wants to close child windows of some type. There is a method in user control code behind:
public void CloseChildWindows(ChildWindowType type)
{
...
}
But I can't call this method as I don't have direct access to the view.
Another solution I think about is to somehow expose user control ViewModel as one of its properties (so I can bind it and give command directly to ViewModel). But I don't want user control users to know anything about user control ViewModel.
So what is the right way to solve this problem?
A reasonable way for purists is creating a service that handles your navigation. Short summary: create a NavigationService, register your view at the NavigationService and use the NavigationService from within the view model to navigate.
Example:
To get a reference to NavigationService you could make an abstraction on top of it (i.e. INavigationService) and register/get it via a IoC. More properly you could even make two abstractions, one that contains the methods for registration (used by the view) and one that contains the actuators (used by the view model).
For a more detailed example you could check out the implementation of Gill Cleeren which heavily depends on IoC:
http://www.silverlightshow.net/video/Applied-MVVM-in-Win8-Webinar.aspx starting at 00:36:30
Most answers to this question involve a state variable that is controlled by the ViewModel and the View acts on changes to this variable. This is good for stateful commands like opening or closing a window, or simply showing or hiding some controls. It doesn't work well for stateless event commands though. You could trigger some action on the rising edge of the signal but need to set the signal to low (false) again or it won't ever trigger again.
I have written an article about the ViewCommand pattern which solves this problem. It is basically the reverse direction of regular Commands that go from the View to the current ViewModel. It involves an interface that each ViewModel can implement to send commands to all currently connected Views. A View can be extended to register with each assigned ViewModel when its DataContext property changes. This registration adds the View to the list of Views in the ViewModel. Whenever the ViewModel needs to run a command in a View, it goes through all registered Views and runs the command on them if it exists. This makes use of reflection to find the ViewCommand methods in the View class, but so does Binding in the opposite direction.
The ViewCommand method in the View class:
This is called from a ViewModel:
The article is available on my website and in an older version on CodeProject.
The included code (BSD licence) provides measures to allow renaming methods during code obfuscation.
One way to achieve this would be for the view-model to request that the child windows should be closed:
The view would then subscribe to its view-model's event, and take care of closing the windows when it's fired.
So here the view-model can ensure the child windows are closed without having any knowledge of the view.
I feel I just found a rather nice MVVM solution to this problem. I wrote a behavior that is exposing a type property
WindowType
and a boolean propertyOpen
. DataBinding the latter allows the ViewModel to open and close the windows easily, without knowing anything about the View.Gotta love behaviors... :)
Xaml:
YellowWindow (Black/Purple alike):
ViewModel, ActionCommand:
OpenCloseWindowBehavior:
I have handled this sort of situation in the past by bringing in the concept of a
WindowManager
, which is a horrible name for it, so let's pair it with aWindowViewModel
, which is only slightly less horrible - but the basic idea is:note: I'm just throwing this together very haphazardly; you'd of course want to tune this idea to your specific needs.
But anywho, the basic premise is your commands can work on the
WindowViewModel
objects, toggle theIsOpen
flag appropriately, and the manager class handles opening/closing any new windows. There are dozens of possible ways to do this, but it's worked in a pinch for me in the past (when actually implemented and not tossed together on my phone, that is)