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

3
我是 MVVM 的新手,长期以来我一直没能得到这个问题的答案。我不知道这个问题是否如此困难,还是我没有解释清楚。我有一个 MainWindow.xaml 文件,其中包含一个文本块和一个按钮,用于接收文本块中的数据,现在当我按下按钮时,它应该打开一个名为 tableView.xaml 的第二个视图(我已经为它创建了用户控件/Xaml)。
我有两个问题(请注意,我在回答问题时遵循 MVVM):
(1)如何通过关闭当前打开的 MainWindow.xaml 表单(此按钮单击使用 MVVM 绑定)来从按钮单击打开此“tableView.xaml”视图?
这里是我的按钮单击代码,新窗体必须在这里打开(通过关闭当前的 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;
            }
        }

但是如何实现呢?

(2) "tableView.Xaml"必须包含GUI,该GUI将根据以前在MainWindow.xaml上通过“保存按钮”接收的输入动态编写c#代码,并且必须添加到tableView.Xaml中。那么应该在哪里编写C#代码,使得这个C#代码生成的GUI将被渲染到tableView.Xaml上呢?

总结: 请问是否能告诉我如何从按钮点击打开tableView.Xaml以及如何将GUI追加/渲染到tableView.Xaml上(我知道如何编写C#代码来生成GUI,但要在符合MVVM规则的情况下编写代码以便在tableView.Xaml上呈现GUI)。

编辑: 我感觉亲爱的帮助者们仍然不能理解我试图做什么,所以请看以下更详细的说明:

我要做的事情是: 我有一个MainWindow.xaml,其中包含文本框“输入表格数量”,它正在接收用户输入(作为他要创建的表格数)。用户在输入此输入后,将单击保存按钮。现在,保存按钮必须关闭MainWindow.xaml并启动新用户控件“tableView.xaml”。假设他输入4并保存。现在,在新启动的tableView.xaml中,我必须显示文本框,该文本框将接收“表名称”,“列数”,如果他为“列数”输入3,则应该还有3个文本框来接收每个列的名称和数据类型以及最后的主键和保存按钮。

并且此GUI现在必须重复4次,因为在Mainwondow.xaml中,用户在开始时输入了4个“表格数量”。因此,我们必须针对“tableView.xaml”中的每个表格条目重复此GUI 4次,因此我正在编写C#代码来生成GUI,并且在从C#代码获取GUI后将渲染该GUI到tableView.xaml上。现在你明白了吗?如果您知道使用MVVM的其他方法,请给我一个小样本。

通过使用C#代码动态生成GUI意味着我必须执行以下操作:

现在,当我从MainWindow.xaml中获得用户输入时,我将重复下面的GUI 4次(在一个大容器内的for循环中),并将其渲染到tableView.Xaml上(我这样做是因为我必须根据MainWindow.xaml表单中用户的选择动态重复它)。

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

这个stp必须转到tableView.xaml,并且必须呈现4个stackpanel,每个包含textblock。

通常,应用程序中有一个主窗口,该窗口不应关闭。例如,像带有托管Page1、Page2等框架的MainWindow的导航系统,或者打开子窗口和对话框的MainWindow。 - Liero
3个回答

2

1) 你的具体需求仍不是很清晰,这使得回答这个问题非常困难。你说你想要“关闭当前的MainWindow.xaml表单”,但你似乎表明你的tableView是一个UserControl。你是在试图关闭MainWindow并完全用完全不同的窗口替换它吗?如果是这样,那么这有什么意义呢?如果原始的MainWindow正在关闭,为什么不只是更改该窗口的内容?

2) 这是一个大话题,再次需要您提供更多关于您具体要做什么的信息,但通常情况下您会使用DataTemplating。您首先声明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; } }
}

然后,您需要创建一个ItemsControl,将ItemsSource绑定到数组上,指定要在其上绘制它们的面板类型,并包含DataTemplates,以指定如何在GUI中对每个VM进行模板化:

<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>

结果:

在此输入图片描述

显然,这只是一个非常简单的示例,仅用于展示一般想法,在实际应用程序中,您还将向视图模型添加字段以指定文本和命令处理程序等内容。

请编辑问题以便我了解我正在尝试做什么。 - struggling

2

首先,您应该定义应用程序的导航系统。它是具有子窗口的主窗口吗?还是带有页面的框架?还是带有选项卡的主窗口?然后创建一个类来覆盖此逻辑。

例如,要从视图模型显示子窗口并传递一些输入,可以执行以下操作:

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 
        });
    }
}

如果您需要导航到另一页而不是打开新窗口,您可以轻松创建NavigationService而不是DialogService。思路相同。
为什么我不直接在viewmodel中创建子窗口?因为窗口是视图,我想保持关注点的分离。
为什么我使用IDialogService而不是具体类? MVVM原则之一是可测试性。在运行时,将使用打开实际窗口的具体类,但在测试中,我可以轻松创建不会打开窗口的模拟。
通常,如果您想从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;
     }

我没有使用任何内置的MVVM库,如(MVVM light或Prism)。如何获取“DialogOptions”和“IDialogService”? - struggling
请修改问题以便我了解您想要做什么。 - struggling
我没有使用任何库。我已经创建了自己的IDialogService接口和实现。基本上,它只是一个简单的类,根据指定的参数显示窗口。 - Liero
@DialoService,它不是内置的吗?但是当我在我的代码中使用时,它会给出未知接口..缺少库或引用。但请阅读代码的编辑部分,即使它适合我的场景? - struggling
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Liero
好的,谢谢。但是这个Switch窗口方法包含什么? - struggling

1

我认为纯粹的MVVM对你来说有点太难以理解和设计过度。你可能无法充分利用它。

让我们尝试通过将MVVM和传统方法结合来简化它:

创建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:

<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; }
    }
}

TableView.xaml文件丢失了,请解释一下在哪里编写C#代码以生成GUI,该GUI将呈现到TableView.xaml中。还要说明如何编写tableView.xaml,使其呈现由该C#代码传递的GUI? - struggling
我不能为您编写整个应用程序。问题是关于打开窗口并传递参数的。视图生成可以在TableView.xaml.cs中完成,也可以通过将控件如datagring绑定到viewmodel来完成。如果您不知道如何操作,请提出新问题。 - Liero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接