我在WPF应用程序中使用MVVM Light工具包。我想知道从现有窗口打开新窗口的最佳方法是什么。我有MainViewModel
,它负责我的申请MainWindow
。现在在MainView
,点击一下按钮,我想打开它上面的第二个窗口。我已将RelayCommmand
绑定到Button
的{{1}}。在Command
的方法中,我可以创建一个新的窗口对象,只需调用RelayCommand
,如下所示:
Show()
但我不认为ViewModel应该负责创建新的var view2 = new view2()
view2.Show()
对象。我已阅读此帖WPF MVVM Get Parent from VIEW MODEL,其中Bugnion建议将消息从view2
传递给view1
,然后viewmodel1
应创建新的view1
。但我不确定将消息传递给view2
是什么意思? view1
应如何处理消息?在它的代码背后或什么?
此致 纳比尔
答案 0 :(得分:56)
将邮件从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();
}
}
}
答案 1 :(得分:4)
你为什么要走这条路?这很简单。如果用toggleButton,超链接或任何其他类似按钮的控件替换按钮,则不需要更新“后面的代码” - 它是MVVM模式的基本原理。在你的新toggleButton(或其他)中,你仍然最终绑定到相同的命令。
例如,我正在为想要拥有2个UI的客户创建一个项目 - 在演示方面,每个UI都会在各方面都有根本的不同。水平标签与垂直RadPanelBar(想想手风琴)进行导航。这两个视图都可以指向相同的viewModel - 当用户单击View 1中的Work Order选项卡时,它会触发在面板栏的Work Order Header中触发的相同“WorkOrderCommand”。
在代码隐藏模型中,您必须编写两个单独的事件。在这里你只需要编码一个。
此外,它允许设计师使用Blend创建他们想要的任何布局。只要他们有钩子(EventToCommand控件),我自己(作为开发人员)就不会关心最终产品的样子。
松耦合非常强大。
答案 2 :(得分:3)
您可以这样做,就像您需要创建一些事件并在视图中注册它们并在视图模型中调用它们。并打开该弹出窗口。
喜欢这个例子
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 +=
,而不是单击双击选项卡按钮
并在寄存器弹出方法中右键打开弹出窗口的代码。
答案 3 :(得分:2)
除非我在这里忽略了这一点 - 如果我要使用后面的代码,那么为什么不直接实现button_click事件并打开第二个视图?
Bugnion似乎建议的是view1 - &gt;按钮单击 - &gt;继电器命令 - &gt; viewmodel1 - &gt;消息 - &gt; view1 - &gt; view1.cs - &gt;开放视图2.
通过编写代码隐藏,无论如何都会牺牲可测试性,那么为什么要走这么长的路?
答案 4 :(得分:2)
您可以使用通用接口将视图特定功能抽象为服务。在视图层中,您可以提供这些服务的具体实例,并使用IoC容器和依赖注入技术构建视图模型。
在您的情况下,您可以构建一个IWindowManager接口或类似的具有所需方法的接口。这可以在您的视图层中实现。我最近写了一篇小博客文章,演示了如何从视图模型中抽象出对话行为。类似的apporach可用于任何与用户界面相关的服务,如Navigation,MessageBoxes等。
此链接可能对您有用http://nileshgule.blogspot.com/2011/05/silverlight-use-dialogservice-to.html
许多人还使用从view.cs文件中订阅的视图模型触发事件的方法,并从那里执行MessageBox或任何其他UI相关操作。我个人喜欢注入服务的方法,因为那样你就可以提供同一服务的多个实现。一个简单的例子是如何在Silverlight和Windows Phone 7应用程序中处理导航。您可以使用相同的视图模型,但会根据应用程序类型注入导航服务的不同实现。
答案 5 :(得分:0)
我找到了解决此问题的最佳方法,即从ViewModel打开和关闭窗口。正如this链接所示,
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
的基本ViewModel,其中包含其他成员。完成后,使用此viewmodel作为其他视图模型的基础。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; })); }
DialogCloser.DialogResult
属性绑定CloseWindowFlag
依赖项属性。然后,您可以从视图模型中打开/关闭/隐藏窗口。