What is the recommended way to open a new window using MVVM in WPF
Asked Answered
P

4

6

Hello everyone and thanks in advance for your time.

I'm currently learning MVVM using WPF for a small Biz App that I'm writing. I have read a lot of articles about the MVVM pattern and found that one of the key areas is to decouple the ViewModel from the View as much as possible.

I want to open a new Window in my app but I'm not sure if I should open it from the ViewModel using an ICommand or directly from the view using a standard event. Someone I work with suggested that that I should use commands, but then I thought that this would mean having a reference to a View in my ViewModel, which according to what I understand is precisely what the MVVM pattern focuses on avoiding.

My understanding is that if a window will open for navigation purposes only and the process of opening that new windows has no effect on the Model, then I should keep all of this on the view using standard events.

I know in sw development everything "depends", but guess my question is there a "right"/standard way of doing this?

Best regards, Daniel

Posset answered 24/1, 2014 at 19:10 Comment(1)
Some further reading on MVVM Light's Messenger classes: msdn.microsoft.com/en-us/magazine/jj694937.aspxPosset
B
5

Yes, VMs should communicate with Views utilizing the Events that Views can subscribe to...

In VM:

public event EventHandler<NotificationEventArgs<string>> DisplayOptionsNotice;  

In View:

private readonly MainViewModel mvm;
...
mvm = DataContext as MainViewModel;
mvm.DisplayOptionsNotice += DisplayOptionsWindow;
...
private void DisplayOptionsWindow(object sender, NotificationEventArgs<string> e)
{
    ...  
    optionsWindow = new OptionsWindow { Owner = this };
    optionsWindow.ShowDialog();
    ...
}
Broadnax answered 24/1, 2014 at 19:22 Comment(6)
Sorry... how? VM only publishes an event it doesn't know what window is supposed to open.Posset
@ReedCopsey How so? VM only publishes the event any interested object can subscibe to... If you're referring to having a reference to VM in the View, VM is already DataContext of the View so it's coupled to it anyway...Broadnax
@DeanK. Yeah -this is a tighter coupling (the View ends up with code behind that requires direct references/knowledge of the VM(s)). I don't necessarily see a problem with it, but this would be "bad" in the eyes of a lot of MVVM purists. You've got a +1 from me, but I thought I'd point that out.Daniels
I'm not supposed to write thank you comments, but my current reputation prevents me from giving you "+1"s, so thank you both.Posset
Just asked a question to see if any MVVM purists can come up with good reasons not to use this pattern... https://mcmap.net/q/1760415/-is-a-reference-in-a-view-to-a-viewmodel-which-is-its-datacontext-in-any-way-detrimental-to-mvvm-pattern/229930Broadnax
I was checking out this article and they use an approach similar to yours for closing a window. social.technet.microsoft.com/wiki/contents/articles/…Posset
D
1

but then I thought that this would mean having a reference to a View in my ViewModel, which according to what I understand is precisely what the MVVM pattern focuses on avoiding.

In general, the way this is handled is via some form of inversion of control. Most MVVM frameworks will provide a service of some form to open a window, and use a Service Locator or Dependency Injection to provide the service to the ViewModel.

This allows your ViewModel to stay decoupled from the specific view rendering framework - you'd pass the service the new VM and say "Show this VM in a window", and that code would be platform specific.

Daniels answered 24/1, 2014 at 19:18 Comment(4)
Thank you, I find your comment very interesting, is there any further reading you'd recommend on this topic?Posset
@Posset Take a look at MVVM Light's Messenger classes for examples of one approach that's used here.Daniels
Forgot to mention that I'm not using a specific MVVM Framework. Thought working on it from scratch was the best way to learn.Posset
@Posset Yeah - I was just pointing out one place to look for ideas. Any DI framework or service locator approach would work, though.Daniels
S
0

As Reed said Service locator or DI will do the work and wont breake the MVVM pattern. from my experience you will have to do three things: First check about the Service Locator or Di see what more friendly for you and implement it.
Second start making the interface of IWindow\IWindowDialog that your view (windows\ Messagebox - if you like) will implement for example.

the last thing is to implement the windows\ messages.

it will take time to do it from scratch (I did it) but if you will focus one thing at a time. you cut the time in half.

Good luck

Seducer answered 24/1, 2014 at 20:38 Comment(0)
S
0

If you've chosen to create a new window from the ViewModel layer, you could do it like this:

  1. Create a generic interface for your window's View:
    public interface IWindow<TViewModel>{}
    
  2. Create the window's View class as the interface implementation with setting the ViewModel as its generic part:
    public partial class MyWindow : IWindow<MyWindowViewModel>
    
  3. Create a service that will find a View based on the ViewModel using reflection:
    public void Show(object viewModel)
    {
        var types = Assembly.GetExecutingAssembly().GetTypes().ToList();
    
        foreach (var view in types)
        {
            var @interface = view.GetInterface(typeof(IWindow<>).Name);
            if (@interface == null)
                continue;
    
            if (@interface.GenericTypeArguments.FirstOrDefault() != viewModel.GetType())
                continue;
    
            var windowObject = Activator.CreateInstance(view);
            if (windowObject is not Window window)
                continue;
    
            window.DataContext = viewModel;
            window.Show();
        }
    }
    
Supinator answered 20/9, 2023 at 14:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.