在模型视图ViewModel中打开新窗口的最佳位置

时间:2009-03-17 16:21:49

标签: wpf design-patterns mvvm

我有一个MVVM应用程序。其中一个ViewModels是填充ObservableCollection的'FindFilesCommand'。然后我在同一个ViewModel中实现'RemoveFilesCommand'。然后,此命令将打开一个窗口以获取更多用户输入。

在保持MVVM范式的同时/最佳方式是什么?不知何故 做:

new WhateverWindow( ).Show( ) 
在ViewModel中

似乎错了。

干杯,

史蒂夫

7 个答案:

答案 0 :(得分:2)

我个人认为这个场景是主窗口视图模型想要为最终用户完成一项任务。

它应该负责创建任务并初始化它。该视图应负责创建和显示子窗口,并将该任务用作新实例化窗口的视图模型。

可以取消或提交任务。它在完成时会发出通知。

该窗口使用通知关闭自身。如果有后续工作,父视图模型会在任务提交后使用通知执行其他工作。

我认为这与人们使用代码隐藏方法所做的自然/直观的事情非常接近,但重构是将独立于UI的问题分解为视图模型,而不会引入额外的概念开销,例如服务等。

我为Silverlight实现了这个功能。有关详细信息,请参阅http://www.nikhilk.net/ViewModel-Dialogs-Task-Pattern.aspx ...我很想听到有关此问题的评论/进一步建议。

答案 1 :(得分:1)

在Jaime Rodriguez和Karl Shifflet的Southridge房地产示例中,他们在视图模型中创建窗口,更具体地说是在绑定命令的执行部分:

    protected void OnShowDetails ( object param ) 
    {
        // DetailsWindow window = new DetailsWindow();
        ListingDetailsWindow window = new ListingDetailsWindow();
        window.DataContext = new ListingDetailsViewModel ( param as Listing, this.CurrentProfile ) ; 
        ViewManager.Current.ShowWindow(window, true); 
    } 

这是链接: http://blogs.msdn.com/jaimer/archive/2009/02/10/m-v-vm-training-day-sample-application-and-decks.aspx

我猜这不是一个大问题。毕竟,Viewmodel充当视图和业务层/数据层之间的“粘合剂”,因此,与视图(UI)耦合是正常的...

答案 2 :(得分:1)

Onyx(http://www.codeplex.com/wpfonyx)将为此提供一个相当不错的解决方案。例如,查看ICommonDialogProvider服务,该服务可以从ViewModel中使用,如下所示:

ICommonFileDialogProvider provider = this.View.GetService<ICommonDialogProvider>();
IOpenFileDialog openDialog = provider.CreateOpenFileDialog();
// configure the IOpenFileDialog here... removed for brevity
openDialog.ShowDialog();

这与使用具体的OpenFileDialog非常相似,但完全可以测试。您真正需要的解耦量将是一个实现细节。例如,在您的情况下,您可能需要一个完全隐藏您正在使用对话框的服务。有点像:

public interface IRemoveFiles
{
   string[] GetFilesToRemove();
}

IRemoveFiles removeFiles = this.View.GetService<IRemoveFiles>();
string[] files = removeFiles.GetFilesToRemove();

然后,您必须确保View具有IRemoveFiles服务的实现,您可以使用多种选项。

Onyx尚未准备好发布,但代码完全正常工作且至少可用作参考点。我希望很快就会发布稳定的V1接口,并且只要我们有合适的文档和样本就会发布。

答案 3 :(得分:0)

我也遇到过MVVM这个问题。我的第一个想法是试图找到一种不使用对话框的方法。使用WPF比使用对话框更容易用更流畅的方式做事。

如果无法做到这一点,最好的选择似乎是让ViewModel调用Shared类来获取用户的信息。 ViewModel应该完全不知道正在显示对话框。

因此,作为一个简单的例子,如果您需要用户确认删除,ViewModel可以调用DialogHelper.ConfirmDeletion(),它将返回用户是否表示是或否的布尔值。对话框的实际显示将在Helper类中完成。

对于更高级的对话框,返回大量数据,辅助方法应返回一个对象,其中包含对话框中的所有信息。

我同意它与MVVM的其余部分不是最顺畅,但我还没有找到更好的例子。

答案 4 :(得分:0)

我不得不说,服务是前往这里的方式。

服务接口提供了一种返回数据的方法。然后,该服务的实际实现可以显示对话框或其他任何内容以获取界面所需的信息。

这种测试方法可以在测试中模拟服务接口,而ViewModel则不是更明智的。就ViewModel而言,它向服务部门询问了一些信息,并收到了所需信息。

答案 5 :(得分:0)

我们正在做的是这样的事情,这里描述的是: http://www.codeproject.com/KB/WPF/DialogBehavior.aspx?msg=3439968#xx3439968xx

ViewModel有一个名为ConfirmDeletionViewModel的属性。一旦我设置了属性,行为就会打开对话框(模态或非模态)并使用ConfirmDeletionViewModel。另外,我传递的是当用户想要关闭对话框时执行的委托。这基本上是一个将ConfirmDeletionViewModel属性设置为null的委托。

答案 6 :(得分:-1)

对于此类对话。我将它定义为FindFilesCommand的嵌套类。如果在许多命令中使用基本对话框,我将其定义在这些命令可访问的模块中,并使命令相应地配置对话框。

命令对象足以显示对话框与其他软件的交互方式。在我自己的软件中,Command对象驻留在它们自己的库中,因此对话框对系统的其余部分是隐藏的。

在我看来,做任何事情都有点过头了。此外,试图将其保持在最高级别,通常涉及创建许多额外的接口和注册方法。这是一个很少获得的编码。

与任何框架一样,奴隶般的奉献会引导你走下一些奇怪的小巷。您需要使用判断来查看当您遇到代码异味时是否还有其他技术可供使用。在我看来,对话框应该紧密绑定并在使用它们的命令旁边定义。这样,五年后,我可以回到代码的那一部分,看看命令正在处理的所有内容。

在少数几个实例中,对话框对多个命令有用,我在所有命令共有的模块中定义它。但是在我的软件中,20个对话框中有1个是这样的。主要的例外是文件打开/保存对话框。如果几十个命令使用了一个对话框,那么我将完全定义一个接口,创建一个表单来实现该接口并注册该表单。

如果国际使用的本地化对您的应用程序很重要,您需要确保使用此方案对此进行说明,因为所有表单都不在一个模块中。