MVVM WPF change window and close the previous

2019-06-11 02:53发布

问题:

As a starter in this all architecture of MVVM I'm having a few doubts regarding the navigation between one window to another. I'm using the Framework MVVM Light.

The behavior I expect is in WinForms like this:

GeneralWindow gw = new GeneralWindow(); this.Hide(); // or close gw.Show();

I already lose a couple of hours trying to find some hints using the messenger, but the methods I found I have to use code-behind in the view and that is not very MVVMish.

Best regards and thank you in advance.

回答1:

The behavior I expect is in WinForms like this:

GeneralWindow gw = new GeneralWindow(); this.Hide(); // or close gw.Show();

MVVM pattern divides View from ViewModel. So it is not eligible to create new View from ViewModel. Creating window instance and showing window from view model is violation of MVVM". So I suggest you to use the popular technique where you can change Views using ContentControl and DataTemplate.

Let's dive in this technique:

<Window>
   <Window.Resources>
      <DataTemplate DataType="{x:Type ViewModelA}">
         <localControls:ViewAUserControl/>
      </DataTemplate>
      <DataTemplate DataType="{x:Type ViewModelB}">
         <localControls:ViewBUserControl/>
      </DataTemplate>
   <Window.Resources>
  <ContentPresenter Content="{Binding CurrentView}"/>
</Window>

If Window.DataContext is an instance of ViewModelA, then ViewA will be displayed and Window.DataContext is an instance of ViewModelB, then ViewB will be displayed.

Let me show an example where it can be seen where you should put DataTemplates:

<Window x:Class="SimpleMVVMExample.ApplicationView"
        ...The code omitted for the brevity...
        Title="Simple MVVM Example with Navigation" Height="350" Width="525">
    <Window.Resources>
       <DataTemplate DataType="{x:Type ViewModelA}">
         <localControls:ViewAUserControl/>
      </DataTemplate>
      <DataTemplate DataType="{x:Type ViewModelB}">
         <localControls:ViewBUserControl/>
      </DataTemplate>
    </Window.Resources>

    <DockPanel>
        <Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
            <ItemsControl ItemsSource="{Binding ListOfViewModels}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Content="{Binding Name}"
                                Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                CommandParameter="{Binding }"
                                Margin="2,5"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Border>

        <ContentControl Content="{Binding CurrentDataTemplateViewModel}" />
    </DockPanel>
</Window>

The best example I've ever seen and read it is made by Rachel Lim. See the example.

Update:

If you want really open new Window, then you should create an intermediate layer to make ViewModel not dependent on a concrete implementation of creating new window.

public class YourViewModel
{
    private readonly IWindowFactory windowFactory;
    private ICommand openNewWindow;

    public YourViewModel(IWindowFactory _windowFactory)
    {
        windowFactory = windowFactory;

        /**
         * Would need to assign value to m_openNewWindow here, and 
         * associate the DoOpenWindow method
         * to the execution of the command.
         * */
        openNewWindow = null;  
    }

    public void DoOpenNewWindow()
    {
        windowFactory.CreateNewWindow();
    }

    public ICommand OpenNewWindow { get { return openNewWindow; } }
}

public interface IWindowFactory
{
    void CreateNewWindow();
}

public class ProductionWindowFactory: IWindowFactory
{

    #region Implementation of INewWindowFactory

    public void CreateNewWindow()
    {
       NewWindow window = new NewWindow
           {
               DataContext = new NewWindowViewModel()
           };
       window.Show();
    }

    #endregion
}

How to close a Window?

There are a lot of approaches.. One of them is:

Application.Current.MainWindow.Close()