WPF MVVM Window navigation

2019-05-30 03:35发布

I'm applying the MVVM pattern to my application that will have dozens of screens (with the respectives ViewModels). Now I'm stuck in a very simple point ... who has the responsibility to create the new window, instantiate the viewModel and assign one to another?

I think is wrong to do this in the View or even in the ViewModel. I saw many replies advising the use of external frameworks, that is not an option to me.

What do you think?

What is the official Windows recommendation?

2条回答
聊天终结者
2楼-- · 2019-05-30 03:49

Let's try an example: the user clicks a button and a confirmation dialog box is shown with Yes/No. Normally you would raise the notification in the event handler of the button:

private void Button_Click(object sender, MouseRoutedEventArgs e)
{
    var result = MessageBox.Show("Confirm?", MessageBoxButton.YesNo);
    if (result == true)
        //something
    else
        //something else
}

Now, since we have MVVM, the business logic (here the if/else) must be moved into the ViewModel. But the UI must remain on the UI control! Suppose this is the ViewModel:

public class VM
{
    public void DoSomething()
    {
        //raise the confirmation interaction
    }
}

The ViewModel can't own the UI... but can own an ABSTRACTION of the interaction required with the user.

This may be the interface of the interaction:

public interface IConfirmationInteraction
{
    bool? RaiseConfirmationRequest(string message);
}

So the ViewModel can have a property like this:

public IConfirmationInteraction ConfirmInteraction { get; }

VM does not instantiate it, VM accepts an instance of the interface passed by someone else. For example in the constructor:

public VM(IConfirmationInteraction confirmInteraction)
{
    if (confirmInteraction == null)
        throw new ArgumentNullException(nameof(confirmInteration));
    ConfirmInteraction = confirmInteraction;
}

So its method can become:

public void DoSomething()
{
    var result = ConfirmInteraction.RaiseConfirmationRequest("Confirm?");
    if (result == true)
        //do something
    else
        //do something else
}

And the UI? You can create a concrete interaction that uses UI elements:

public class UIConfirmationInteraction : IConfirmationInteraction
{
    public bool? RaiseConfirmationRequest(string message)
    {
        return MessageBox.Show(message, MessageBoxButton.YesNo);
    }
}

Do you see the point? We have saved the pattern: the ViewModel acts with the logic, putting together different abstractions, but knows nothing about the implementations, about message boxes, buttons, etc.. You can implement those interactions as UI interactions, for example in the constructor of the UI Control that owns the VM:

public class MyControl : USerControl
{
    public MyControl()
    {
        DataContext = new VM(new UIConfirmationInteraction());
    }
}

But you can also implement them as automatic results, for example when you want to run a test list trying a default answer of "yes" or a default answer of "no":

public class YesConfirmationInteraction : IConfirmationInteraction
{
    public bool? RaiseConfirmationRequest(string message)
    {
        return true;
    }
}

This is called "dependency injection". try it on Google, you can find dozens of tutorials. In this case, I've build a dependency injection by constructor.

This is a solid way in which you can build manually all the bridges between the UI controls, through the ViewModels, but preserving the pattern.

查看更多
三岁会撩人
3楼-- · 2019-05-30 03:58

this is what i do:

  • 1 mainwindow with mainviewmodel as application root
  • Loginview with viewmodel when needed
  • viewmodel first approach for all childs/modules
  • dialogservice viewmodel driven for dialogs
  • windowservice viewmodel driven for windows

the mainviewmodel compose the modules via MEF.

查看更多
登录 后发表回答