I have a static WindowService
class which helps me to create new windows and modal dialogs.
So far, what I have is this:
/// <summary>
/// Opens a new window of type <paramref name="newWindowType"/> and closes the <paramref name="oldWindow"/>
/// </summary>
/// <param name="oldWindow">The window which should be closed (Usually the current open window)</param>
/// <param name="newWindowType">The type of the new window to open</param>
public static void ShowNewWindow(Window oldWindow, Type newWindowType)
{
((Window)Activator.CreateInstance(newWindowType)).Show();
oldWindow.Close();
}
My viewmodel raises an event and the view is subscribed to it. In the event handler in the view, it calls WindowService.ShowNewWindow(this,The type here)
. This works fine.
My modal dialog creating method will also work in a similar way. The only difference is that the information will be returned to the view (At the event handler) so the view will have to pass that information to the view model in code explicitly. This violates mvvm pattern and I don't know how to make the viewmodel wait for the view to return the value after the event is raised.
Is there a better way of doing this?
Ah, this ol' chestnut.
There are many different variations on how to achieve this, however here's my two cents.
The main ideas here are to ensure that your
View
andView Model
do not know about each other, therefore yourView
should not subscribe to an event in yourView Model
, and yourView Model
should not directly call your service and provide a viewType
.Don't use events, use Commands instead
My recommendation would be to use
ICommand
implementations instead of relying on a static service class, for the reason that your class will always have a dependency to this service, and also as soon as you send the viewType
to this service, then the MVVM pattern is lost.So, firstly, we need some kind of command which will open a window of a given
Type
, here's what I have come up with:The class inherits from
ICommand
and specifies the implementation which accepts aType
, which represents the desiredView
that we want to open. Notice I have marked a method asvirtual
, I'll explain that part in a moment.Here's how we can make use of this command in our
View Model
:Now we've created the command, we simply need to bind a
Button
to it:One thing to note here is that I am using
x:Type
as theCommandParameter
, this is theWindow
that will be created when this command gets executed.But what about a dialog?
What we achieved above is only half of the requirement, we now need something that will display a dialog and output the result to our
View Model
, this isn't so tricky as we have most of what we need already in our existingOpenWindowCommand
.First, we need to create the command:
We're making use of our
OpenWindowCommand
by inheriting from it and using it's implementation instead of having to copy all of it into our new class. The command takes anAction
which is a reference to a method in yourView Model
, you have the option of defining an action before or after (or both) a dialog is displayed.The next step is to change our
View Model
so it creates this new command:The usage of this command is practically the same as before, but it just references a different command:
And there you have it, everything is loosely coupled, the only real dependencies here are that your
View Model
depends on yourICommand
classes.Some final words
The
ICommand
classes that I have created act as a controller between theView
and theView Model
to ensure that they do not know about each other, and keeps the MVVM pattern enforced.Like I said at the beginning of this answer, there are many ways of which this can be achieved, however I hope you are now a little more enlightened.