How to open one view from another view using MVVM in WPF

可紊 提交于 2019-12-04 14:51:30

1) You're still not being very clear in your exact requirements which makes it very difficult to answer this question. You say you want to "close the current MainWindow.xaml form" yet you seem to indicate your tableView is a UserControl. Are you trying to shut down MainWindow and replace it entirely with a completely different window? If so then what's the point? If the original MainWindow is closing why don't you just change the contents of that Window?

2) This is a big topic and again you're going to have to provide more information as to exactly what you're trying to do, but in general you use DataTemplating. You start by declaring view models for the GUI elements you wish to create, as well a parent VM that contains a list of them:

public abstract class GuiItem { } // base class

public class TextBlockVM : GuiItem { }
public class CheckBoxVM : GuiItem { }
public class TextBoxVM : GuiItem { }

public class CustomViewModel : ViewModelBase
{
    private GuiItem[] _GuiItems = new GuiItem[]
    {
        new TextBlockVM{},
        new CheckBoxVM{},
        new TextBoxVM{}
    };
    public GuiItem[] GuiItems { get { return this._GuiItems; } }
}

Then you create an ItemsControl, binding ItemsSource to the array, specifying what type of panel you want to draw them on and containing DataTemplates that specify how to template each VM in the GUI:

<ItemsControl ItemsSource="{Binding GuiItems}" Margin="10">

    <ItemsControl.Resources>

        <DataTemplate DataType="{x:Type local:TextBlockVM}">
            <TextBlock Text="This is a TextBlock" />
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:CheckBoxVM}">
            <CheckBox>This is a CheckBox</CheckBox>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:TextBoxVM}">
            <TextBox Width="100" HorizontalAlignment="Left"/>
        </DataTemplate>

    </ItemsControl.Resources>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

</ItemsControl>

Result:

Obviously this is a very simple example just to show the general idea, in a real-world app you'd also add fields to the view models to specify the text and command handlers etc

Ok, first of all, you should define navigation system of your app. Is it main windows with child windows? is it frame with pages? Is it main window with tabs? Then create class that will cover this logic.

E.g. to show child window from viewmodel and pass some inputs to it, you can do following:

public class MainWindowViewModel
{
    private IDialogService _dialogService;
    public MainWindowViewModel(IDialogService dialogService)
    {
        _dialogService = dialogService;
        SaveCommand = new DelegateCommand(Save);
    }

    //implement PropertyChanged if needed
    public string SomeInput { get; set; }

    public DelegateCommand SaveCommand { get; private set; }

    private void Save()
    {
        var tableViewModel = new TableViewModel();
        tableViewModel.SomeInput = this.SomeInput;

        _dialogService.ShowModal(new DialogOptions
        {
            Title = "Table view",
            Content = tableViewModel 
        });
    }
}

if you needed to navigate to another page rather than open new window, you can easily create NavigationService instead of DialogService. Idea is the same.

Why am I not creating child window directly in viewmodel? because window is view and I want to keep separation of concerns.

Why am I using IDialogService instead of concrete class? One of the MVVM principles is testability. at runtime, concrete class that opens real windows will be used, but in test I can easily create mock, that won't open windows.

In general, if you want to close window from viewmodel, you create and call some event (e.g CloseRequested) in viewmodel and window should listen to the event:

public class MainWindowViewModel
{
    public event EventHandler CloseRequested;

    private void Close()
    {
        var closeRequested = CloseRequested;
        if (closeRequested != null) closeRequested (this, EventArgs.Empty);
    }
}

//mainwinow.xaml.cs
     public MainWindow()
     {
         InitializeComponent();

         var viewModel = new MainWindowViewModel();
         viewModel.CloseRequested += (sender, args) => this.Close();
         DataContext = viewModel;
     }

I believe that pure MVVM is a little too difficult and overengineered for you yet. You are probably not able to take full advantage of it.

Let's try to simplify it by combining MVVM and classical approach:

Create MainWindow.xaml, MainWindowViewModel, TableView.xaml and TableViewModel:

public class MainWindowViewModel 
{    
   //implement INotifyPropertyChanged if needed
   public int NumberOfRows { get; set; }
   public int NumberOfColumns { get; set; }

    public void Save()
    {
        //do something if needed
    }
}

MainWindow.xaml:

<StackPanel>
    <TextBlock Text="Rows" />
    <TextBox Text="{Binding NumberOfRows}" />

    <TextBlock Text="Columns" />
    <TextBox Text="{Binding NumberOfColumns}" />

    <Button Content="Save" Click="Save_Click" />
</StackPanel>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }

    MainWindowViewModel ViewModel
    {
        get { return (MainWindowViewModel)DataContext; }
    }

    private void Save_Click(object sender, RoutedEventArgs e)
    {
        ViewModel.Save();

        var tableViewWindow = new TableView(ViewModel.NumberOfRows, ViewModel.NumberOfColumns);
        this.Close();
        tableViewWindow.Show();
    }
}

TableView.xaml.cs

public partial class TableView: Window
{

    public TableView(int rows, int colums)
    {
        InitializeComponent();

        DataContext = new TableViewModel();
        //do whatever needed with rows and columns parameters. 
        //You will probably need then in TableViewModel
    }

    TableViewModel ViewModel
    {
        get { return (TableViewModel )DataContext; }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!