2010-08-02 144 views
43

我在我的WPF应用程序中使用MVVM Light工具包。我想知道从现有窗口打开新窗口的最佳方法是什么。我有这MainViewModel,这是我的应用程序的MainWindow负责。现在在MainView,点击一个按钮,我想打开第二个窗口。我有RelayCommmand绑定到ButtonCommand。在RelayCommand的方法,我可以创造一个新的窗口对象,调用Show(),是这样的:如何使用MVVM Light Toolkit打开一个新窗口

var view2 = new view2() 
view2.Show() 

,但我不认为视图模型应该是负责创建新view2对象。我已阅读此帖WPF MVVM Get Parent from VIEW MODEL,其中Bugnion建议将消息​​从viewmodel1传递到view1,然后view1应创建新的view2。但我不知道他通过将消息传递给view1是什么意思? view1应该如何处理消息?在它的代码背后或什么?

问候, 纳比勒

+0

请参阅http://stackoverflow.com/questions/16993433/mvvm-light-wpf-binding-multiple-instances-of-a-window-to-a-viewmodel/16994523#16994523 – reggaeguitar 2014-04-24 22:07:08

回答

50

传递从ViewModel1到视图1的消息的装置使用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(); 
     } 
    } 
} 
+0

感谢您的回应马特。除了使用消息传递之外,是否还有其他方法/最佳实践在mvvm中打开新视图? – nabeelfarid 2010-08-03 08:16:31

+8

我见过人们使用的另一种方法是使用用于打开视图的类的服务风格。您的ViewModel将与此服务的界面一起工作,以显示ChildWindow,MessageBox或其他任何内容。这是那些在视图的代码隐藏中想要零代码的人的特别喜爱。此外,它可以测试一些,因为你可以模拟服务并声明显示你的视图的方法被调用。 – 2010-08-03 13:03:22

+2

是的,我看到人们也在谈论它。但我不明白这种方法是,当你从一个视图模型使用一些服务打开一个子窗口让我们说IDialogService.OpenChild(),你将如何设置子窗口的所有者,作为调用IDialogService的视图模型。 OpenChild()不知道或没有对其自己的视图的引用? – nabeelfarid 2010-08-04 11:57:37

2

除非我在这里错过了点 - 如果我是背后使用的代码,那么为什么不直接实现button_click事件,并打开第二个看法?

什么比尼翁似乎暗示是厂景 - >右键点击 - >继电器命令 - > viewmodel1 - >信息 - >厂景 - > view1.cs - >打开查看2.

你要牺牲的可测性无论如何编写代码隐藏,为什么要采取这样一个漫长的路线?

+4

漫长的路线将确保: 当您测试您的视图模型时,您至少可以测试广播的消息/请求以打开新视图。您可以将消息请求代码包装在IDialogService中,以便在测试期间使其可嘲弄。 – nabeelfarid 2010-08-25 09:23:11

+2

我同意普拉茨的观点,采取如此漫长的路线有点疯狂。 – Vincent 2010-10-28 17:53:11

+1

对于有少量窗口/视图的小应用程序,该方法后面的代码很好。如果你有一个主窗口只是偶尔打开第二个窗口来显示一些细节,那么增加的复杂性看起来有点过分。如果应用程序变大,后面的代码将不能很好地扩展,测试会受到影响。 – srock 2013-09-05 14:37:29

3

你可以这样做,就像你需要创建一些事件,并在视图中注册那些事件,然后在model.and中调用这些事件并打开弹出窗口。

像这样的例子

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

注册时喜欢这里thake mn.POpUpEvent +=比标签按钮双倍时间内单击

,并在寄存器中弹出方法适合开放代码弹出窗口。

4

你为什么走这条路?这很简单。如果用toggleButton或超链接或任何其他数量的按钮式控件代替按钮,则不需要更新“后面的代码” - 它是MVVM模式的基本原理。在新的toggleButton(或其他)中,您仍然会绑定到同一个确切的Command。

例如,我正在为希望拥有2个UI的客户创建一个项目 - 在演示方面,每种方式都会有根本的不同。水平制表符与垂直RadPanelBar(认为是Accordion)进行导航。这两种视图都可以指向相同的viewModel - 当用户单击视图1中的工作订单选项卡时,它将激发面板栏中工作订单标题中触发的相同“WorkOrderCommand”。

在代码隐藏模型中,您必须编写两个单独的事件。在这里你只需要编码一个。

此外,它允许设计师使用Blend创建他们想要的任何布局。只要他们具有钩子(EventToCommand控件),我自己(作为开发人员)就不会在乎最终产品的外观。

松耦合非常强大。

2

您可以使用通用接口将视图特定功能抽象为服务。在视图层中,您可以提供这些服务的具体实例,并使用IoC容器和依赖注入技术构建视图模型。

在你的情况下,你可以构建一个接口IWindowManager或类似的具有所需方法的接口。这可以在你的视图层中实现。我最近写了一篇小博客文章,演示了如何将对话行为从视图模型中抽象出来。类似apporach可以用于像导航任何用户界面相关的服务,消息框等

此链接可能会有所帮助你http://nileshgule.blogspot.com/2011/05/silverlight-use-dialogservice-to.html

许多人还使用击发这些都对订阅视图模型事件的方法view.cs文件,然后从那里执行MessageBox或任何其他UI相关的操作。我个人喜欢注入服务的方法,因为那样你就可以提供同一服务的多个实现。一个简单的例子就是如何在Silverlight和Windows Phone 7应用程序中处理导航。您可以使用相同的视图模型,但根据应用程序类型注入不同的导航服务实现。

0

我发现最好的方法来解决这个问题,从ViewModel打开和关闭窗口。如this链路所暗示的,

  1. 创建DialogCloser
 
    public static class DialogCloser 
    { 
     public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged)); 

     private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      var window = d as Window; 
      if (window != null) window.Close(); 
     } 

     public static void SetDialogResult(Window target, bool? value) 
     { 
      target.SetValue(DialogResultProperty, value); 
     } 
    } 
  • 创建一个基视图模型从GalaSoft.MvvmLight.ViewModelBase与有额外的成员继承。完成后,使用此视图模型作为其他视图模型的基础。
  •  
        bool? _closeWindowFlag; 
        public bool? CloseWindowFlag 
        { 
         get { return _closeWindowFlag; } 
         set 
         { 
          _closeWindowFlag = value; 
          RaisePropertyChanged("CloseWindowFlag"); 
         } 
        } 
    
        public virtual void CloseWindow(bool? result = true) 
        { 
         Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
         new Action(() => 
         { 
          CloseWindowFlag = CloseWindowFlag == null ? true : !CloseWindowFlag; 
         })); 
        }
  • 在视图中,绑定与在基视图模型的CloseWindowFlag属性DialogCloser.DialogResult依赖项属性。
  • 然后,您可以从视图模型中打开/关闭/隐藏窗口。