在WPF中使用MVVM打开新窗口的推荐方式是什么

问题描述 投票:0回答:4

大家好,提前感谢您的宝贵时间。

我目前正在为我正在编写的一个小型商务应用程序使用 WPF 学习 MVVM。我读了很多关于 MVVM 模式的文章,发现其中一个关键领域是尽可能将 ViewModel 与 View 解耦。

我想在我的应用程序中打开一个新窗口,但我不确定是否应该使用 ICommand 从 ViewModel 打开它,或者使用标准事件直接从视图打开它。与我一起工作的人建议我应该使用命令,但后来我认为这意味着在我的 ViewModel 中引用视图,根据我的理解,这正是 MVVM 模式重点避免的。

我的理解是,如果打开一个窗口仅用于导航目的,并且打开新窗口的过程对模型没有影响,那么我应该使用标准事件将所有这些保留在视图上。

我知道在软件开发中一切都“取决于”,但我猜我的问题是否有一个“正确”/标准的方法来做到这一点?

最诚挚的问候, 丹尼尔

c# wpf events mvvm icommand
4个回答
5
投票

是的,虚拟机应该利用视图可以订阅的事件与视图进行通信...

在虚拟机中:

public event EventHandler<NotificationEventArgs<string>> DisplayOptionsNotice;  

视图中:

private readonly MainViewModel mvm;
...
mvm = DataContext as MainViewModel;
mvm.DisplayOptionsNotice += DisplayOptionsWindow;
...
private void DisplayOptionsWindow(object sender, NotificationEventArgs<string> e)
{
    ...  
    optionsWindow = new OptionsWindow { Owner = this };
    optionsWindow.ShowDialog();
    ...
}

1
投票

但后来我认为这意味着在我的 ViewModel 中引用 View,根据我的理解,这正是 MVVM 模式重点要避免的。

一般来说,处理这种情况的方式是通过某种形式的控制反转。大多数 MVVM 框架将提供某种形式的服务来打开窗口,并使用服务定位器或依赖注入向 ViewModel 提供服务。

这允许您的 ViewModel 与特定视图渲染框架保持分离 - 您可以将服务传递给新 VM 并说“在窗口中显示此 VM”,并且该代码将是特定于平台的。


0
投票

正如 Reed 所说,服务定位器或 DI 将完成这项工作,并且不会破坏 MVVM 模式。 根据我的经验,你必须做三件事: 首先检查服务定位器或 Di 看看什么对您更友好并实施它。
第二步开始制作您的视图(windows\ Messagebox - 如果您愿意)将实现的 IWindow\IWindowDialog 界面。

最后一件事是实现windows\消息。

从头开始需要时间(我做到了),但如果你一次专注于一件事。 你把时间减少了一半。

祝你好运


0
投票

如果您选择从 ViewModel 层创建新窗口,您可以这样做:

  1. 为您的窗口视图创建通用界面:
    public interface IWindow<TViewModel>{}
    
  2. 创建窗口的 View 类作为接口实现,并将 ViewModel 设置为其通用部分:
    public partial class MyWindow : IWindow<MyWindowViewModel>
    
  3. 创建一个服务,该服务将使用反射基于 ViewModel 查找视图:
    public void Show(object viewModel)
    {
        var types = Assembly.GetExecutingAssembly().GetTypes().ToList();
    
        foreach (var view in types)
        {
            var @interface = view.GetInterface(typeof(IWindow<>).Name);
            if (@interface == null)
                continue;
    
            if (@interface.GenericTypeArguments.FirstOrDefault() != viewModel.GetType())
                continue;
    
            var windowObject = Activator.CreateInstance(view);
            if (windowObject is not Window window)
                continue;
    
            window.DataContext = viewModel;
            window.Show();
        }
    }
    
© www.soinside.com 2019 - 2024. All rights reserved.