20

I've been wondering about this for a while... What's the best practice for opening a new window (view & viewmodel) from another viewmodel IF we keep in mind that the viewmodel which opens the new window is not aware of the existence of that view (as it should be).

Thanks.

Singleton
  • 3,701
  • 3
  • 24
  • 37
Jens
  • 3,249
  • 2
  • 25
  • 42

5 Answers5

13

I prefer to use an action delegate that is inserted via the ViewModel constructor. this also means we can easily verify during unit-testing:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new MainViewModel(() => (new Window()).Show()); // would be actual window
    }
}

public class MainViewModel
{
    private Action popupAction;
    public MainViewModel(Action popupAction)
    {
        this.popupAction = popupAction;
    }

    public ICommand PopupCommand { get; set; }

    public void PopupCommandAction()
    {
        popupAction();
    }
}

public class SomeUnitTest
{
    public void TestVM()
    {
        var vm = new MainViewModel(() => { });
    }
}
Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
  • I think using a more full observer pattern would be a lot cleaner hear similar to that within an MVP pattern. For some reason I'm still uncomfortable with the word popup in my ViewModel as it implies something about what the View is doing presentation-wise. – jpierson Dec 02 '10 at 03:44
  • 2
    +1 for elegance (not using third party libraries) and showing something I haven't seen done before. – basarat Dec 04 '10 at 04:59
  • This looks like a very elegant solution, definitely +1 and sharing. – Noich Jan 10 '13 at 13:53
5

I don't use the ViewModel to open another View/ViewModel. This is in the responsibility of a Controller. The ViewModel can inform the Controller (e.g. via Event) that the user expects to see the next View. The Controller creates the View/ViewModel with the help of an IoC Container.

How this works is shown in the ViewModel (EmailClient) sample application of the WPF Application Framework (WAF).

jbe
  • 6,976
  • 1
  • 43
  • 34
3

Use the mediator pattern such as mvvmlight's messenger class:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

The basic idea is the viewmodel sends a message to its view. The receiving view then looks like this:

OnMsgRecived() {

  Viewmodel vm = New Viewmodel() - Or use dependency injection to resolve

  View v = new View()
  v.DataContext = vm
  v.Show()
}

This allows the viewmodel that sent the message to show another window with no 'knowledge' of how or who did the opening.

dnndeveloper
  • 1,631
  • 2
  • 19
  • 36
2

I personally prefer to either raise events in my ViewModel to signal to the view that it needs to do something like open a window. I try not to do this directly though so that I don't see things like an event named OpenWindow in my ViewModel because that seems to violate the separate between the View and the ViewModel in my opinion. Instead what I might do is have a Property that changes state and raises a PropertyChanged event accordingly in which the view may listen to and then decide to open a window in reaction to this signal. In some cases the opening of a window is not related to something in the ViewModel at all and is only a function of the View. In these cases I'm not afraid at all to place code for opening another view within the code-behind portion of the View.

The mediator pattern just makes this more loosely coupled and allows for possibilities where a main application window View or a highly nested View could listen to messages globally within the application without having direct access to ViewModels in order to attach event handlers and such. To filter out messages that are irrelevant you can look at some sort of message source value or some other indication of where the message originates from. For those comfortable with the idea of Windows Messages and how that works between different controls (Windows) within unmanaged and WinForms development might be a way of understanding a system could be built up on top of a mediator that broadcasts messages.

jpierson
  • 16,435
  • 14
  • 105
  • 149
  • 1
    I agree that events are the way to go here. That way you don't have to have one "know" about the other. Prism does a good job of giving yo a nice eventing system to do stuff like this. – Vaccano Dec 03 '10 at 17:33
0

I agree with a mediator-like approach, but the OnMsgReceived is clearly handled in the code-behind of the view, is there a good way to avoid this?

W1ck3dHcH
  • 26
  • 1
  • I think the code-behind of the view is the correct place to be opening other Views. I feel that it would be wrong for a ViewModel for example to spawn a View either directly or indirectly. Instead it's best to raise events or in this case dispatch a message and allow the listeners to do as they wish in reaction to the signal. Remember one of the big goals with MVVM is testability and popping up a window in the View isn't generally part of what needs to be tested in an automated test. – jpierson Dec 02 '10 at 03:48