简体   繁体   English

如何在WPF中使用MVVM从另一个视图打开一个视图

[英]How to open one view from another view using MVVM in WPF

I am new to MVVM. 我是MVVM的新手。 And i am not able to get answer of this problem since a long time. 很长一段时间以来,我无法得到这个问题的答案。 I don't know if question is so difficult or i have not explained properly. 我不知道问题是否如此困难,或者我没有正确解释。 I have MainWindow.Xaml which contains a textblock and a button to receive the data from textblock and now when i press a button it should open second View called tableView.xaml (I have already created User Control for it/Xaml) . 我有MainWindow.Xaml,它包含一个文本块和一个按钮来接收来自textblock的数据,现在当我按下一个按钮时,它应该打开第二个名为tableView.xaml的视图(我已经为它创建了用户控件/ Xaml)。

I have two question now (Please note that i am following MVVM while answering)? 我现在有两个问题(请注意我在回答时关注MVVM)?

(1) How to open this View "tableView.xaml" from button click by closing the current opened MainWindow.xaml form (this button click is binded using MVVM) ? (1)如何通过关闭当前打开的MainWindow.xaml表单从按钮单击打开此视图“tableView.xaml”(此按钮单击是否使用MVVM绑定)?

My code for button click where this new form must open (by closing the current MainWindow.xaml) is here (so i guess the code for opening tableView.xaml must be somewhere here only): 这个新表单必须打开的按钮单击的代码(通过关闭当前的MainWindow.xaml)就在这里(所以我想打开tableView.xaml的代码必须只在这里的某处):

 public void SaveExecuted() //some where here i have to open "tableView.Xaml"
 {
   System.Windows.MessageBox.Show(string.Format("Saved: {0} {1} ", DB_Col.DbName, DB_Col.NumTables));
 }
 public ICommand SavePersonCommand
        {
            get
            {
                if (savePersonCommand == null)
                    savePersonCommand = new DelegateCommand(new Action(SaveExecuted), new Func<bool>(SaveCanExecute));
                return savePersonCommand;
            }
        }

But how to do that ? 但是怎么做呢?

(2) The "tableView.Xaml" has to contain the GUI which will be written in c# dynamically according to the input i received in previous MainWindow.xaml on "Save Button" click and they must be added to tableView.Xaml. (2)“tableView.Xaml”必须包含GUI,它将根据以前MainWindow.xaml在“保存按钮”上单击时收到的输入动态写入c#,并且必须将它们添加到tableView.Xaml中。 So how and where to write that C# code so that the GUI generated by this c# code will be rendered on tableView.Xaml ? 那么如何以及在何处编写C#代码,以便这个c#代码生成的GUI将在tableView.Xaml上呈现?

Summary: Could some one please let me know how to open the tableView.Xaml from Button click and how to append/render the GUI on tableView.Xaml (I know how to write the c# code to Generate GUI but where to write that code such that it will render GUI in tableView.Xaml respecting the MVVM rules). 简介:有人可以告诉我如何从Button单击打开tableView.Xaml以及如何在tableView.Xaml上追加/呈现GUI(我知道如何编写c#代码来生成GUI但是在哪里编写代码如此它将在tableView.Xaml中呈现符合MVVM规则的GUI)。

EDIT: I feel like still dear helpers are not able to understand what i am trying to do, So please see it for more detail : 编辑:我觉得仍然亲爱的帮助者无法理解我想要做什么,所以请看更详细的信息:

What i have to do is: I have MainWindow.xaml which contains textbox "Enter number of tables" which is receiving input from user (as Number of tables he want to create).And user after entering this input he will click save button. 我要做的是:我有MainWindow.xaml,它包含文本框“输入表数”,它接收来自用户的输入(作为他想要创建的表的数量)。用户输入此输入后,他将单击保存按钮。 Now saving the button must close the MainWindow.xaml and will launch new usercontrol which is "tableView.xaml". 现在保存按钮必须关闭MainWindow.xaml并启动新的usercontrol,即“tableView.xaml”。 Suppose he enter 4 and save. 假设他输入4并保存。 Now in newly launched tableView.xaml i have to show text box which will receive "Name Of table", "Nmbr of columns", if he enter 3 for Number of columns, then there should be 3 more text box to receive name of each column and data type and Primary key and save button at last. 现在在新启动的tableView.xaml中我必须显示文本框,它将收到“表名”,“Nmbr of columns”,如果他为列数输入3,那么应该还有3个文本框来接收每个名称列和数据类型以及主键和保存按钮。

And this GUI now has to repeat 4 times because in Mainwondow.xaml the user enter 4 in "Number of tables" option dynamically at starting. 此GUI现在必须重复4次,因为在Mainwondow.xaml中,用户在启动时动态地在“Number of tables”选项中输入4。 So we have to repeat this GUI 4 times for each table entry in "tableView.xaml" that's why i am writing code in c# for GUI generation and after GUI generation i will render that GUI obtained from C# code to tableView.xaml. 因此,我们必须为“tableView.xaml”中的每个表条目重复此GUI 4次,这就是我在c#中编写用于GUI生成的代码的原因,并且在GUI生成之后,我将从C#代码获取的GUI呈现给tableView.xaml。 Now you understood ? 现在你明白了吗? If you know any other way of doing this using MVVM then you are most welcome to give me a little sample. 如果您知道使用MVVM执行此操作的任何其他方式,那么欢迎您给我一些示例。 By generating GUI dynamically using C# code mean i have to do something like : 通过使用C#代码动态生成GUI意味着我必须执行以下操作:

Now when i got Input from user in MainWindow.xaml as 4 the i repeat below GUI 4 times (in for loop inside a big container) and render it to tableView.Xaml (I am doing it in c# because i have to repeat it dynamically according to the user's choice in MainWindow.xaml form). 现在当我从MainWindow.xaml中的用户输入4作为4时,我在GUI下面重复4次(在一个大容器内循环)并将其呈现给tableView.Xaml(我在c#中这样做,因为我必须动态地重复它根据用户在MainWindow.xaml表单中的选择)。

StackPanel stp = new StackPanel();
TextBlock txt1 = new TextBlock();
stp.Children.Add(txt1);  

This stp must go to tableView.xaml and must render 4 stackpanel each containing textblock. 这个stp必须转到tableView.xaml,并且必须渲染每个包含textblock的4个stackpanel。

1) You're still not being very clear in your exact requirements which makes it very difficult to answer this question. 1)你仍然不是很清楚自己的确切要求,因此很难回答这个问题。 You say you want to "close the current MainWindow.xaml form" yet you seem to indicate your tableView is a UserControl. 你说你想“关闭当前的MainWindow.xaml表单”但你似乎表明你的tableView是一个UserControl。 Are you trying to shut down MainWindow and replace it entirely with a completely different window? 您是否正在尝试关闭MainWindow并使用完全不同的窗口完全替换它? If so then what's the point? 如果是这样那么重点是什么? If the original MainWindow is closing why don't you just change the contents of that Window? 如果原始MainWindow正在关闭,为什么不改变该窗口的内容?

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. 2)这是一个很大的主题,你将不得不提供更多关于你正在尝试做什么的信息,但一般来说你使用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: 首先声明要创建的GUI元素的视图模型,以及包含它们列表的父VM:

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绑定到数组,指定要在其上绘制的面板类型,并包含指定如何在GUI中模拟每个VM的DataTemplates:

<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. 然后创建将涵盖此逻辑的类。

Eg to show child window from viewmodel and pass some inputs to it, you can do following: 例如,要从viewmodel显示子窗口并将一些输入传递给它,您可以执行以下操作:

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. 如果您需要导航到另一个页面而不是打开新窗口,则可以轻松创建NavigationService而不是DialogService。 Idea is the same. 想法是一样的。

Why am I not creating child window directly in viewmodel? 为什么我不直接在viewmodel中创建子窗口? because window is view and I want to keep separation of concerns. 因为窗口是视图,我想保持关注点的分离。

Why am I using IDialogService instead of concrete class? 为什么我使用IDialogService而不是具体的类? One of the MVVM principles is testability. MVVM原则之一是可测试性。 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 (eg CloseRequested) in viewmodel and window should listen to the event: 通常,如果要从viewmodel关闭窗口,则在viewmodel中创建并调用一些事件(例如CloseRequested),窗口应该监听事件:

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. 我相信纯粹的MVVM对你来说有点过于困难和过度设计。 You are probably not able to take full advantage of it. 你可能无法充分利用它。

Let's try to simplify it by combining MVVM and classical approach: 让我们尝试通过结合MVVM和经典方法来简化它:

Create MainWindow.xaml, MainWindowViewModel, TableView.xaml and TableViewModel: 创建MainWindow.xaml,MainWindowViewModel,TableView.xaml和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: 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: 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 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; }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM