如何使用 MVVM Light Toolkit 打开一个新窗口
Posted
技术标签:
【中文标题】如何使用 MVVM Light Toolkit 打开一个新窗口【英文标题】:How to open a new window using MVVM Light Toolkit 【发布时间】:2011-03-24 02:46:28 【问题描述】:我在我的 WPF 应用程序中使用 MVVM Light 工具包。我想知道从现有窗口打开新窗口的最佳方法是什么。我有这个MainViewModel
,它负责我的应用程序的MainWindow
。现在在MainView
中,单击按钮,我想在其顶部打开第二个窗口。我将RelayCommmand
绑定到Button
的Command
。在RelayCommand
的方法中,我可以创建一个新的窗口对象并简单地调用Show()
,如下所示:
var view2 = new view2()
view2.Show()
但我认为 ViewModel 不应该负责创建新的 view2
对象。我已经阅读了这篇帖子WPF MVVM Get Parent from VIEW MODEL,Bugnion 建议将消息从viewmodel1
传递给view1
,然后view1
应该创建新的view2
。但我不确定他将消息传递给view1
究竟是什么意思? view1
应该如何处理消息?在它背后的代码还是什么?
问候, 纳比尔
【问题讨论】:
见***.com/questions/16993433/… 【参考方案1】:从 ViewModel1 向 View1 传递消息意味着使用messaging capabilities in the MVVM Light Toolkit。
例如,您的 ViewModel1 可能有一个名为 ShowView2Command 的命令,然后它会发送一条消息来显示视图。
public class ViewModel1 : ViewModelBase
public RelayCommand ShowView2Command private set; get;
public ViewModel1() : base()
ShowView2Command = new RelayCommand(ShowView2CommandExecute);
public void ShowView2CommandExecute()
Messenger.Default.Send(new NotificationMessage("ShowView2"));
View1 会在其后面的代码中注册接收消息,并在收到正确消息时显示 View2。
public partial class View1 : UserControl
public View1()
InitializeComponent();
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
private void NotificationMessageReceived(NotificationMessage msg)
if (msg.Notification == "ShowView2")
var view2 = new view2();
view2.Show();
【讨论】:
感谢马特的回复。除了使用消息传递之外,还有其他方法/最佳实践可以在 mvvm 中打开新视图吗? 我看到人们使用的另一种方法是使用类的服务样式来打开视图。您的 ViewModel 将使用此服务的接口来显示 ChildWindow、MessageBox 或其他任何内容。这是那些希望在他们的视图代码隐藏中使用零代码的人特别喜欢的。此外,它更易于测试,因为您可以模拟服务并断言显示视图的方法已被调用。 是的,我也看到人们在谈论它。但是我在这种方法中不明白的是,当您使用某些服务从视图模型打开子窗口时,比如说 IDialogService.OpenChild(),您将如何设置子窗口的所有者,作为调用 IDialogService 的视图模型。 OpenChild() 不知道或不引用自己的视图? 嗯,有多种原因需要设置子窗口的所有权。所有权关系强制执行某些行为,包括最小化、最大化和恢复等(msdn.microsoft.com/en-us/library/…)。至于使用 RootVisual,这又意味着向视图发送消息,因为 rootvisual 可以在代码隐藏中访问,而不是在 viewmodel 中? 这很酷,但同时也让我想起了 MFC,大约在 1997 年使用消息泵......【参考方案2】:你为什么要走这条路?这很简单。如果您将按钮替换为切换按钮、超链接或任何其他数量的类似按钮的控件,则无需更新“隐藏代码”——这是 MVVM 模式的基本原则。在您的新切换按钮(或其他)中,您最终仍会绑定到相同的命令。
例如,我正在为一个想要拥有 2 个 UI 的客户创建一个项目 - 就演示而言,一个在各个方面都将完全不同。用于导航的水平选项卡与垂直 RadPanelBar(想想 Accordion)。这两个视图都可以指向同一个视图模型 - 当用户单击视图 1 中的工作订单选项卡时,它会触发在面板栏中的工作订单标题中触发的相同“WorkOrderCommand”。
在代码隐藏模型中,您必须编写两个单独的事件。在这里,您只需编写一个代码。
此外,它允许设计师使用 Blend 来创建他们想要的任何布局。只要他们有钩子(EventToCommand 控件),我自己(作为开发人员)就不会关心最终产品的样子。
松散耦合非常强大。
【讨论】:
【参考方案3】:您可以这样做,就像您需要创建一些事件并在 view 中注册这些事件并在视图模型中调用它们。然后打开该弹出窗口。
喜欢这个例子
public class Mainclass : MainView
public delegate abc RegisterPopUp(abc A);
public RegisterPopUp POpUpEvent ;
public RelayCommand ShowCommand private set; get;
public void ShowCommand()
ShowCommand("Your parameter");
在视图内MainView mn=new MainView();
在这里注册事件就像mn.POpUpEvent +=
而不是双击标签按钮
并在右侧注册弹出方法中打开弹出窗口的代码。
【讨论】:
【参考方案4】:除非我错过了这里的重点——如果我要使用后面的代码,那为什么不直接实现 button_click 事件并打开第二个视图呢?
Bugnion 似乎暗示的是 view1 -> button click -> relay command -> viewmodel1 -> message -> view1 -> view1.cs -> open view 2.
无论如何你都会通过编写代码隐藏来牺牲可测试性,那么为什么要走这么长的路呢?
【讨论】:
漫长的路线将确保:当您测试视图模型时,您至少可以测试已广播消息/请求以打开新视图。您可以将消息请求代码包装在 IDialogService 中,使其在测试期间可模拟。 我同意普拉茨的观点,走这么长的路有点疯狂。 代码隐藏方法适用于具有少量窗口/视图的小型应用程序。如果您的主窗口只是偶尔打开第二个窗口只是为了显示一些细节,那么增加的复杂性似乎有点过头了。如果应用变大,后面的代码将无法很好地扩展,测试也会受到影响。【参考方案5】:您可以使用通用接口将视图特定功能抽象为服务。在视图层中,您可以提供这些服务的具体实例,并使用 IoC 容器和依赖注入技术构建视图模型。
在您的情况下,您可以构建一个接口 IWindowManager 或类似的具有所需方法的东西。这可以在您的视图层中实现。我最近写了一篇小博文,演示了如何从视图模型中抽象出对话行为。类似的方法可用于任何与用户界面相关的服务,如导航、消息框等。
此链接可能对您有所帮助http://nileshgule.blogspot.com/2011/05/silverlight-use-dialogservice-to.html
许多人还使用从 view.cs 文件上订阅的视图模型触发事件的方法,并从那里执行 MessageBox 或任何其他与 UI 相关的操作。我个人喜欢注入服务的方法,因为这样您就可以提供同一服务的多个实现。一个简单的例子是如何在 Silverlight 和 Windows Phone 7 应用程序中处理导航。您可以使用相同的视图模型,但根据应用程序类型注入不同的导航服务实现。
【讨论】:
【参考方案6】:我发现解决这个问题的最佳方法是从 ViewModel 打开和关闭窗口。正如this 链接所暗示的,
-
创建一个
DialogCloser
类
-
创建一个继承自
GalaSoft.MvvmLight.ViewModelBase
的基础视图模型,其中包含其他成员。完成后,将此视图模型用作其他视图模型的基础。
-
在视图中,将
DialogCloser.DialogResult
依赖属性与基础视图模型中的CloseWindowFlag
属性绑定。
然后您可以在视图模型中打开/关闭/隐藏窗口。
【讨论】:
你能提供一个打开窗口的例子吗?据我了解,这只会关闭窗口/视图。以上是关于如何使用 MVVM Light Toolkit 打开一个新窗口的主要内容,如果未能解决你的问题,请参考以下文章
如何将 RelayCommand 与 MVVM Light 框架一起使用