【问题标题】:Good examples of MVVM TemplateMVVM模板的好例子
【发布时间】:2010-12-12 08:23:04
【问题描述】:

我目前正在使用 Microsoft MVVM 模板,发现缺少详细示例令人沮丧。包含的 ContactBook 示例显示了非常少的命令处理,我发现的唯一其他示例来自 MSDN 杂志文章,其中概念相似,但使用的方法略有不同,并且仍然缺乏任何复杂性。有没有像样的 MVVM 示例,至少展示了基本的 CRUD 操作和对话/内容切换?


大家的建议真的很有用,我会开始整理一份好的资源清单

框架/模板

实用文章

截图

其他库

【问题讨论】:

    标签: wpf mvvm


    【解决方案1】:

    不幸的是,没有一个伟大的 MVVM 示例应用程序可以做所有事情,并且有很多不同的方法来做事。首先,您可能想熟悉其中一个应用程序框架(Prism 是一个不错的选择),因为它们为您提供了方便的工具,如依赖注入、命令、事件聚合等,可以轻松尝试适合您的不同模式.

    棱镜释放:
    http://www.codeplex.com/CompositeWPF

    它包括一个相当不错的示例应用程序(股票交易员)以及许多较小的示例和操作方法。至少它很好地展示了人们用来使 MVVM 实际工作的几种常见子模式。我相信他们有 CRUD 和对话的例子。

    Prism 不一定适用于每个项目,但熟悉它是件好事。

    CRUD: 这部分非常简单,WPF 两种方式的绑定使得编辑大多数数据变得非常容易。真正的诀窍是提供一个易于设置 UI 的模型。至少您要确保您的 ViewModel(或业务对象)实现 INotifyPropertyChanged 以支持绑定,并且您可以将属性直接绑定到 UI 控件,但您可能还希望实现 IDataErrorInfo 以进行验证。通常,如果您使用某种 ORM 解决方案,则设置 CRUD 很容易。

    本文演示了简单的 crud 操作: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

    它是基于 LinqToSql 构建的,但这与示例无关 - 重要的是您的业务对象实现 INotifyPropertyChanged(由 LinqToSql 生成的类执行)。 MVVM 不是该示例的重点,但我认为在这种情况下它并不重要。

    本文演示数据验证
    http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx

    同样,大多数 ORM 解决方案生成的类已经实现了IDataErrorInfo,并且通常提供一种机制来轻松添加自定义验证规则。

    大多数情况下,您可以获取由某个 ORM 创建的对象(模型)并将其包装在一个 ViewModel 中,该 ViewModel 包含它和用于保存/删除的命令 - 您已准备好将 UI 直接绑定到模型的属性。

    视图看起来像这样(ViewModel 有一个属性 Item 保存模型,就像在 ORM 中创建的类):

    <StackPanel>
       <StackPanel DataContext=Item>
          <TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
          <TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
       </StackPanel>
       <Button Command="{Binding SaveCommand}" />
       <Button Command="{Binding CancelCommand}" />
    </StackPanel>
    

    对话框: 对话框和 MVVM 有点棘手。我更喜欢在对话框中使用 Mediator 方法的风格,您可以在 StackOverflow 问题中了解更多信息:
    WPF MVVM dialog example

    我常用的做法,不是很经典的MVVM,可以总结如下:

    对话框 ViewModel 的基类,它公开提交和取消操作的命令,让视图知道对话框已准备好关闭的事件,以及所有对话框中需要的任何其他内容。

    对话框的通用视图 - 这可以是一个窗口,也可以是一个自定义的“模态”覆盖类型控件。它的核心是我们将视图模型转储到的内容呈现器,它处理关闭窗口的连接 - 例如,在数据上下文更改时,您可以检查新的 ViewModel 是否从您的基类继承,如果是,订阅相关的关闭事件(处理程序将分配对话结果)。如果您提供替代的通用关闭功能(例如 X 按钮),您应该确保也在 ViewModel 上运行相关的关闭命令。

    您需要为您的 ViewModel 提供数据模板的地方,它们可能非常简单,尤其是因为您可能有一个封装在单独控件中的每个对话框的视图。 ViewModel 的默认数据模板将如下所示:

    <DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
       <views:AddressEditView DataContext="{Binding}" />
    </DataTemplate>
    

    对话框视图需要访问这些,否则它将不知道如何显示 ViewModel,除了共享对话框 UI 之外,它的内容基本上是这样的:

    <ContentControl Content="{Binding}" />
    

    隐式数据模板会将视图映射到模型,但谁启动它?

    这是不那么 mvvm 部分。一种方法是使用全局事件。我认为更好的做法是使用通过依赖注入提供的事件聚合器类型设置 - 这样事件对于容器来说是全局的,而不是整个应用程序。 Prism 使用统一框架进行容器语义和依赖注入,总体来说我还是挺喜欢 Unity 的。

    通常,根窗口订阅此事件是有意义的 - 它可以打开对话框并将其数据上下文设置为通过引发事件传入的 ViewModel。

    以这种方式设置允许 ViewModel 要求应用程序打开一个对话框并在其中响应用户操作,而无需了解有关 UI 的任何信息,因此在大多数情况下 MVVM 保持完整。

    但是,有时 UI 必须弹出对话框,这会使事情变得有点棘手。例如,如果对话框位置取决于打开它的按钮的位置。在这种情况下,您需要在请求打开对话框时提供一些特定于 UI 的信息。我通常会创建一个单独的类来保存 ViewModel 和一些相关的 UI 信息。不幸的是,那里的一些耦合似乎是不可避免的。

    一个按钮处理程序的伪代码,它引发一个需要元素位置数据的对话框:

    ButtonClickHandler(sender, args){
        var vm = DataContext as ISomeDialogProvider; // check for null
        var ui_vm = new ViewModelContainer();
        // assign margin, width, or anything else that your custom dialog might require
        ...
        ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
        // raise the dialog show event
    }
    

    对话框视图将绑定到位置数据,并将包含的 ViewModel 传递给内部ContentControl。 ViewModel 本身对 UI 仍然一无所知。

    一般来说,我不会使用ShowDialog() 方法的DialogResult 返回属性,也不会期望线程阻塞直到对话框关闭。非标准模态对话框并不总是这样工作,在复合环境中,您通常不希望事件处理程序无论如何都像那样阻塞。我更喜欢让 ViewModel 处理这个问题——ViewModel 的创建者可以订阅其相关事件、设置提交/取消方法等,因此无需依赖这种 UI 机制。

    所以不是这个流程:

    // in code behind
    var result = somedialog.ShowDialog();
    if (result == ...
    

    我用:

    // in view model
    var vm = new SomeDialogViewModel(); // child view model
    vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit 
    vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
    // raise dialog request event on the container
    

    我更喜欢这种方式,因为我的大多数对话框都是非阻塞的伪模态控件,并且这样做似乎比解决它更简单。也易于进行单元测试。

    【讨论】:

    • 感谢您的详细解答!我最近发现我最大的问题是当我需要让 MainViewModel 与其他视图模型通信以处理应用程序的流程时。然而,似乎 MVVM + Mediator 似乎是流行的方法。
    • Mediator 肯定有帮助,事件聚合器模式(Prism 有一个很好的实现)在以低耦合为目标时也很有帮助。此外,您的主视图模型通常有自己的子视图模型,与它们通信应该没有问题。当您的子视图模型需要与您的应用程序中他们不一定知道的其他模块进行交互时,您需要使用调解器或/和事件聚合器 - 包括 UI(我的对话框示例是关于这种特殊情况的)。
    • 使用对话框和窗口的指南非常有帮助。但是,我遇到了一些问题: 1. 如何从视图中设置窗口标题? 2. 设置所有者窗口如何处理?
    • @Daniel Skinner:我假设您在这里谈论对话,如果我错了,请纠正我。对话框标题只是另一个属性,您可以将其绑定到您喜欢的任何内容。如果您按照我的方法使用基本对话框视图模型类(假设它具有标题属性),那么在您的所有通用对话框窗口中,您可以使用 UI 到 UI 绑定将标题设置为 {Binding Path=DataContext.Title, ElementName= NameOfContentPresenter}。所有者窗口有点小技巧 - 这意味着实际弹出对话框的中介需要了解根应用程序视图。
    • 事实上,我收回了这一点——不管你在某个时候如何构建它,实际上弹出对话框的人都需要引用根应用程序窗口/视图。请注意我所说的“通常,根窗口订阅此事件是有意义的 - 它可以打开对话框并将其数据上下文设置为通过引发事件传入的视图模型。”您可以在此处设置所有者。
    【解决方案2】:

    我在代码项目上从头开始编写了一个简单的 MVVM 示例,这里是链接MVVM WPF step by step。 它从一个简单的 3 层架构开始,让您可以使用 PRISM 等框架。

    【讨论】:

      【解决方案3】:

      这里我添加了一个 WPF(Inventory Management App) 应用程序的链接,它使用了我设计的 MVVM 架构

      它的用户界面很棒。 https://github.com/shivam01990/InventoryManagement

      【讨论】:

        【解决方案4】:

        发现这个有用。也有代码。

        http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

        【讨论】:

        【解决方案5】:

        在我把这件事交给我之前,即使我也有同样的挫败感。我启动了 IncEditor。

        IncEditor (http://inceditor.codeplex.com) 是一个尝试向开发人员介绍 WPF、MVVM 和 MEF 的编辑器。我启动它并设法获得一些功能,如“主题”支持。我不是 WPF 或 MVVM 或 MEF 方面的专家,所以我不能在其中添加很多功能。我真诚地请求你们把它做得更好,以便像我这样的疯子能更好地理解它。

        【讨论】:

          【解决方案6】:

          Jason Dolinger 在 MVVM 方面做得很好 screencast。就像 Egor 提到的那样,没有一个很好的例子。他们都结束了。大多数都是很好的 MVVM 示例,但当您遇到复杂问题时则不然。每个人都有自己的方式。 Laurent Bugnion 也有一种在视图模型之间进行通信的好方法。 http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch 也是一个很好的例子。 Paul Stovel 有一个很好的 post,他的 Magellan 框架也解释了很多。

          【讨论】:

            【解决方案7】:

            我也分享了你的挫败感。我正在写一个应用程序,我有这 3 个要求:

            • 可扩展
            • 带有 MVVM 的 WPF
            • GPL 兼容示例

            我发现的都是零碎的,所以我才开始尽我所能写出来。在我深入了解之后,我意识到可能还有其他人(比如你自己)可以使用参考应用程序,因此我将通用的东西重构为 WPF/MVVM 应用程序框架并在 LGPL 下发布。我将其命名为SoapBox Core。如果您转到下载页面,您会看到它带有一个小型演示应用程序,并且该演示应用程序的源代码也可供下载。希望对您有所帮助。另外,如果您想了解更多信息,请发送电子邮件至 scott {at}soapboxautomation.com。

            编辑:还发布了CodeProject article 解释它的工作原理。

            【讨论】:

              【解决方案8】:

              你看过Caliburn吗? ContactManager 示例中有很多好东西。通用 WPF 示例还很好地概述了命令。文档相当好,论坛很活跃。推荐!

              【讨论】:

                【解决方案9】:

                Cinch framework 中的示例项目显示了基本的 CRUD 和导航工具。这是使用 MVVM 的一个相当好的示例,其中包含一个 multi-part article 解释其用法和动机。

                【讨论】:

                  猜你喜欢
                  • 2010-09-29
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-09-27
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-02-23
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多