views:

438

answers:

2

Hi guys,

I am using MVVM light toolkit in my wpf application. I would like to know what is the best approach for opening a new window from an existing window. I have got this MainViewModel, which is respnsible for MainWindow of my application. Now in the MainView, on a button click, I would like to open a second window on top of it. I have got RelayCommmand binded to the button command. In the relay command method, I can create a new window object and simply call show, something like this

var view2 = new view2()
view2.Show()

but I don't think the viewmodel should be responsible for creating the new view2 object. I have read this post http://stackoverflow.com/questions/2761162/wpf-mvvm-get-parent-from-view-model where Bugnion has suggested to pass message to the view1 from the viewmodel1 and then view1 should create the new view2. But I am not sure what does he actually mean by passing the message to the view1? how should the view1 handle the message? In its code behind or what?

Regards, Nabeel

+2  A: 

Passing a message from ViewModel1 to View1 means to use the messaging capabilities in the MVVM Light Toolkit.

For example, your ViewModel1 could have a command called ShowView2Command, then it would send a message to display the view.

public class ViewModel1 : ViewModelBase
{
    public RelayCommand ShowView2Command { private set; get; }

    public ViewModel1() : base()
    {
        ShowView2Command = new RelayCommand(ShowView2CommandExecute);
    }

    public void ShowView2CommandExecute()
    {
        Messenger.Default.Send(new NotificationMessage("ShowView2"));
    }
}

View1 would register to receive messages in its code behind and display View2 when it receives the correct message.

public partial class View1 : UserControl
{
    public View1()
    {
        InitializeComponent();
        Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
    }

    private void NotificationMessageReceived(NotificationMessage msg)
    {
        if (msg.Notification == "ShowView2")
        {
            var view2 = new view2();
            view2.Show();
        }
    }
}
Matt Casto
Thanks for the response Matt. Are there any other approaches/best practices for opening new views in mvvm apart from using messaging ?
nabeelfarid
Another approach that I've seen people using is to have a service style of class that is used to open the view. Your ViewModel would work with the interface of this service to display the ChildWindow, MessageBox, or whatever. This is a particular favorite of those who want zero code in their view's code-behind. Also, its a bit more testable since you can mock the service and assert that the method to display your view was called.
Matt Casto
yeah I have seen people talking about it too. But what I do not understand in this approach is that when you are opening a child window from a view model using some service lets say IDialogService.OpenChild(), how would you setup the owner of the child window, as the viewmodel calling IDialogService.OpenChild() does not know or have a reference to its own view?
nabeelfarid
You don't have to set an owner for a ChildWindow, or at least I've never had to. If you must have an owner, you could probably get the App's root visual and use that.
Matt Casto
Well there are various reasons why one would need to set the ownership of a child window. The ownsership relationship enforces certain behaviors, including with respect to minimizing, maximizing, and restoring etc.(http://msdn.microsoft.com/en-us/library/system.windows.window.owner.aspx). As for using the RootVisual, that again means sending message to the view, because rootvisual will be accessible in codebehind, not in viewmodel?
nabeelfarid
You can call App.Current.RootVisual from anywhere. However, this does add a dependency into your ViewModel class that should be encapsulated for unit testing.
Matt Casto
+1  A: 

Unless I am missing the point here - if I were to use the code behind, then why not directly implement button_click event and open the second view?

What Bugnion seems to be suggesting is view1 -> button click -> relay command -> viewmodel1 -> message -> view1 -> view1.cs -> open view 2.

You are going to sacrifice testability anyhow by writing code-behind, so why take such a long route?

Pratz
the long route will make sure that :when you are testing your viewmodel, you can atleast test that a message/request has been broadcasted to open new view. You can wrap the message request code in an IDialogService to make it mockable during testing.
nabeelfarid
I agree with Pratz, it's a bit crazy to take such a long route.
Vincent