【问题标题】:Opening a new window on button click WPF MVVM在按钮上打开一个新窗口单击 WPF MVVM
【发布时间】:2016-05-18 08:29:06
【问题描述】:

我正在学习 WPF MVVM,并希望在主窗口单击按钮时打开一个新窗口。

我知道每个 View 都必须有一个等效的 ViewModel,而 MVVM 的基本原则之一是 ViewModel 不得对 View 有任何了解。

所以请任何人都可以提供一个简单的干净示例,它不违反任何关于如何创建两个视图和两个具有以下功能的视图模型的 MVVM 原则:

通过单击主视图中的按钮来显示新视图。

【问题讨论】:

  • 当谈到为 MVVM 打开新窗口时,它变得很棘手。主要原因之一是DataTemplate 不允许您在内部定义Window 派生类。网上有很多文章提供了许多不同的方法。我建议先尝试阅读它们。每种方法都有其优点和缺点,通常它们也不遵循 100% MVVM 原则。

标签: c# wpf xaml mvvm


【解决方案1】:

您可以创建一个单独的服务来将视图作为对话框启动,以便可以在整个应用程序中以通用方式使用它。并将通过想要启动任何对话框的构造函数将此服务注入到 ViewModel。

public interface IDialogWindowService<T>
{
    void Show();
    void ShowDialog();
}

public class DialogWindowService<T> : IDialogWindowService<T> where T : Window
{
    public void Show()
    {
        container.Resolve<T>().Show();
    }

    public void ShowDialog()
    {
        container.Resolve<T>().ShowDialog();
    }
}

现在只需将此服务注入相应的 ViewModel。

public class YourViewModel
{
    //commands
    public ICommand someCommand { get; set; }

    private IDialogWindowService<BookingView> _dialogService;
    public YourViewModel(IDialogWindowService<YourView > dialogService)
    {
        _dialogService = dialogService
        someCommand = new RelayCommand(someCommandDoJob, () => true);
    }

    public void someCommandDoJob(object obj)
    {
        //Since you want to launch this view as dialog you can set its datacontext in its own constructor.    
        _dialogService.ShowDialog();
    }
}

您可以使用DataTemplates 更改视图。它允许根据ViewModel动态切换Views

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

如果 Window.DataContext 是 ViewModelA 的实例,则 ViewA 将被显示并

Window.DataContext 是 ViewModelB 的一个实例,然后会显示 ViewB。

我见过和读过的最好的例子是 Rachel Lim 的。 See the example.

【讨论】:

  • 您在 ViewModel 中引用了一个视图:private IDialogWindowService _dialogService;这不是违反 MVVM 吗?
  • @whitefang1993 你能显示我在 viewModel 中引用视图的位置吗?
  • 在 YourViewModel 类中,“IDialogWindowService”类型引用“_dialogService”用于类型 BookingView。假设您在另一个项目中有 BookingView,您是否必须添加对该项目的引用才能使用“IDialogWindowService”的引用?因此,如果您有两个项目,一个用于 ViewModels,一个用于 Views,您必须在 ViewModels 项目中引用 Views 项目,以便您可以在 YourViewModel 中使用 BookingView。如果我误解了什么,请纠正我。我只是想澄清一些事情。
  • @whitefang1993 是的,你是对的。我忘了这件事。请参阅此示例。 stackoverflow.com/questions/16652501/open-a-new-window-in-mvvm
【解决方案2】:

根据您的使用情况,从视图的代码隐藏打开视图没有任何问题。毕竟还是查看代码

MyView view = new MyView();
view.Show();

否则,如果您需要从ViewModel 或使用ICommand 打开一个窗口,那么您可以查看我在@ 上写的“Open Windows and Dialogs in MVVM” 库987654321@。这将演示如何使用 MVVM 设计模式通过单击按钮来打开Window

【讨论】:

  • 我也是 WPF 和 MVVM 的新手。但据我了解,这是一种非常糟糕的方法。原因是当你这样做时,你的 ViewModel 变得不可单元测试。如果你拉取整个 ViewModel 文件夹并将其放入一个不存在 View 的新项目中,那么你的代码将无法编译和运行。
  • 我不确定您是否完全理解我的回答。代码隐藏是指视图的代码,而不是视图模型代码。
  • 对不起,我的错,我以为代码来自 ViewModel。
【解决方案3】:

我过去取得的一些成功是创建基本上是一个视图工厂,它构造一个视图并为其分配一个视图模型。这给了我一个一站式的服务来拼接视图,就像你使用 IoC 一样。

这可能有优点和缺点,所以我很想知道是否有其他/更好的方法,但到目前为止,这是我发现最不痛苦的做法。

【讨论】:

    【解决方案4】:

    要成为更纯粹的 MVVM 实现,您可以使用工厂或为每个视图编写接口驱动的控制器,以处理 a) 要显示的内容和 b) 如何将其绑定到数据。所以对于控制器:FormAlpha 有 FormAlphaViewModel 和 FormAlpha.VPS。这个接口非常适合这个,因为每个实现都是不同的。

    因此,创建一个标准,使每个视图都有一个以 vps 或视觉呈现服务结尾的接口驱动类。并且您想在该按钮单击时触发 FormAlpha。您将使用反射或工厂将 FormAlpha.vps 作为 IVisualPresentor 加载并调用 IVisualPreentor.Display(parms);

    每个 VPS 都有加载特定表单的工作,将数据绑定与与该表单关联的视图模型绑定(提供的解决方案并不多),并且第一个参数始终具有 Show() 或 ShowDialog() 的必需参数.

    而且...当然...您的视图模型得到的第一件事就是传递的参数。所以 VPS 会分解出这些参数并确认传递了正确的参数。通过这种方式(从您的示例中),您拥有按钮代表并想要部署的功能。 VPS 处理完成此任务所需的数据和视觉效果。调用者只知道 VPS 将处理该函数是什么。现在我只是从内存中弄乱了下面的代码,所以它并不完美。但是提出了我上面所说的想法。

    public interface IVisualPresentor
    { void Display(params object[] parms) };
    
    public class FormAlpha.VPS : IVisualPresentor
    {  
         public void Display(params object[] parms)
         {
           //validate parms if needed and cast to type specific data
           //for example it needs session data parms[1] and customer parms[2]
           var form = new FormAlpha();
           var model = new FormAlphaViewModel( sessionData, customer );
           form.DataBinding = model;
           if ((bool)parms[0])
              form.Show(); 
           else 
              form.ShowDialog();
        }
     }
    

    【讨论】:

      【解决方案5】:

      假设 Form1 是您的主窗体,Form2 是您要打开的窗体: 您可以使用此代码(将其插入 Form1):

      Form2 frm = new Form2();
      frm.Show();
      

      另外,如果你想关闭主窗体,你可以使用this.Close();(它会关闭当前窗体)。 此外,如果您只想打开 Form2 但仍要将 Form1 作为主要的,您可以使用:

      Form2 frm = new Form2();
      frm.Show();
      this.Focus();
      

      【讨论】:

      • 这不是 MVVM。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 2015-09-09
      • 2018-12-18
      • 1970-01-01
      • 2018-02-20
      • 2014-11-08
      • 2015-03-03
      相关资源
      最近更新 更多