WPF - MVVM 视图模型设置

Posted

技术标签:

【中文标题】WPF - MVVM 视图模型设置【英文标题】:WPF - MVVM Viewmodel setup 【发布时间】:2013-11-22 11:58:04 【问题描述】:

我的 mvvm 应用程序当前使用的设置存在一些问题。看到这里的一些帖子,我觉得我可能做的有点不对。

我有几个模型,其中包含子模型列表,例如:

项目 - 包含形式列表 形式 - 包含已订购物品的发货列表 装运 - 包含集装箱列表 容器 - 包含一个包列表

我们目前没有任何与这些模型直接相关的视图模型,我们只是有代表模型列表的视图模型,例如,我们有一个仅包含形式列表的 proformalistviewmodel。

我的问题是,通过这种设置,我对哪个视图模型应该拥有哪些数据有点困惑,例如 ProfomalistViewModel 引用了当前选定的项目,这些模型的所有数据管理(加载和形式列表的保存)是通过通过 DI 加载的管理器类完成的。

我的问题是我是否应该遵循我所看到的内容并拥有一个包含形式列表的 ProjectViewModel 和一个包含发货和有序物品列表的 ProformaViewModel 等等。

这样做的原因是,最初我们没有链接任何模型,项目不拥有形式列表,而是通过经理使用选定的项目 ID(使用关系数据库)单独加载,我们'目前正在将模型更改为我上面描述的系统。

【问题讨论】:

【参考方案1】:

视图模型应该是特定功能区域的用户交互模型

例如,如果您有一个项目列表页面,并且用户可以执行某些操作,例如删除项目编辑项目打印有关项目 那么您应该设计一个包含与此接口相关的数据和操作的视图模型:

例如视图模型应包含:

 * A bindable container for the project data (list of projects)
 * Actions that handle edit/delete interaction
 * An action to handle the print functionality

这些操作中的实际功能可能不包含在视图模型中(VM 可能已经收到注入的服务,例如打印服务或项目存储库),但执行这些操作的责任在于 VM。

可能还需要将每个数据项(项目)包装在视图模型中,以便可以添加其他与交互相关的属性/操作 - 例如“选定”属性(假设用户想要多选视图中的项目 - 您可以向 ProjectViewModel 添加一个选定的属性,该属性将包装每个项目,这使得绑定变得容易)

您最终可能会得到以下结果:

public class ProjectOverviewViewModel 

    public IList<ProjectViewModel> Projects  get;set; 

    public ProjectViewModel SelectedProject  get;set;

    public void EditSelected() 
    
       // Code to open edit page for the selected project
    

    public void Print()
    
    

以及带有可选属性的ProjectViewModel

public class ProjectViewModel

    // Either put the actual data item in here and wrap it:
    public Project Project get;set;

    // Or copy properties onto the viewmodel using automapper or some other mapping framework...
    // or manually :(
    // e.g. properties mirrored from the entity object:
    public int ProjectId  get;set;
    public string ProjectName  get;set;

    // The selected property - now your 'Selected' logic is a function of the view/viewmodel
    // not the entity. The entity should only be concerned with data persistence
    public bool IsSelected get;set;

您可能还希望将视图模型组合在一起以构建更复杂的视图。假设您有一个项目页面和一个“参与项目的用户”页面,并且您想要另一个并排显示的页面(并允许您单击将刷新用户窗格的项目) - 这可以通过合成视图模型(通过创建另一个视图模型,其中包含两个视图模型作为属性并连接两者之间的交互)

public class ProjectAndUserOverView

    public ProjectOverviewViewModel ProjectOverview get;set;
    public ProjectUsersViewModel ProjectUsers get;set;

    // Code here to listen for property changes in ProjectOverview and if SelectedProject changes
    // call ProjectUsersViewModel to refresh the data for the selected user

最终你只是对用户交互进行建模,你可以做得越模块化,就越容易编写更简洁、更可维护的代码

有一些很好的 MVVM 框架 - 我个人最喜欢的是 Caliburn Micro,因为它使上述工作变得非常简单(默认情况下它大量使用约定)并且很容易进入。

【讨论】:

谢谢,这实际上消除了一些困惑,措辞简洁明了,而且我知道您的名字已经回答了我的许多其他问题。谢谢! p.s 根据您在另一个问题中的建议,我已经在使用 caliburn.micro,我喜欢它。 我可能会澄清一下,以防有人对“选定”属性感到困惑 - ProjectOverviewViewModel 中的 SelectedProject 将是用户单击的行,以便他们可以单击编辑按钮,而ProjectViewModel 上的Selected 属性将是网格上的“行选择器”,用于多选操作(例如复选框列)。通常你会有一个或另一个,但不是两者兼有 - 但你可以同时拥有(Grid 控件上的 SelectedItem 绑定到 SelectedProject,每行的复选框列绑定到 Selected)跨度> 【参考方案2】:

MVVM 是一种设计模式,它有 3 个部分:模型、视图模型、视图。图表如下所示:

http://en.wikipedia.org/wiki/Model_View_ViewModel#Pattern_description

您错误地使用了 ViewModel。只有用于显示的数据应该在 ViewModel 中。 例如您的模型:

public class Project 

    public Proforma Prget;set;


public class Proforma

    public string Nameget; set;;

你有 View 用于项目显示(我将 ViewModel 注入构造函数,你可以使用 DataContext 代替):

public partial class ProjectView

    private ProjectViewModel vm;
    public ProjectView(ProjectViewModel vm)
    
        this.vm = vm;
    

如果您想在项目视图上显示形式名称,您应该在 ViewModel 中将其作为字符串提供。 公共类 ProjectViewModel 私人项目公关; 公共字符串 ProformaNamegetreturn pr.Pr.Name;

如果您提供类似形式的形式,您的视图将了解模型。这将违反模式。

【讨论】:

但是我在数据网格中显示了形式列表,所以视图模型有一个项目,项目有一个 ObservableCollection,视图模型向视图公开,视图永远不会知道模型为它只是绑定到 viewmodel 公开的模型列表。 我不完全同意这一点:虽然我同意分离,但在这种情况下,视图并没有真正使用模型。如果您要用不同的对象替换 ViewModel 中的模型引用,只要它具有相同的属性,绑定仍然可以工作。如果您通过将代码隐藏到 直接引用模型类型的视图中来直接影响模型,那么视图/模型将被耦合而违反模式。绑定是一个完全不同的球类游戏,为了绑定几个属性而创建视图模型有时可能是矫枉过正。【参考方案3】:

我的五分钱是 MVVM 是一种模式,而不是一种宗教。我尽可能地使用它并且有意义。 MVVM 的许多部分是未定义的(例如来自命令的用户交互),并且我阅读了很多关于创建 ViewModel 以适应 MVVM 的内容(这会增加设计和对象数量)。我建议您多考虑 DataContext,例如“全局感兴趣的选择保存在全局 DataContext 中,Proforma 相关数据保存在 Proforma DataContext 中”等等,其中 DataContext 是某种 ViewModel。最后,您可能最终会使用 UI 来装配它们。

【讨论】:

看看这是我一直迷路的地方,如何共享全局信息,如果我正确地关注你,你建议有一个包含全局数据的类,比如活动形式,并构建一个视图模型用那个? 是的 - 至少,我这样做是出于实际原因。我的应用程序的许多部分都需要进行选择(例如您的 Proforma),我的特定 DataContexts (=VMs) 具有对全局数据的引用,以便我可以在任何地方访问它。我不知道 MVVM 对此有什么看法,但我相处得很好。 我认为,由于 ViewModel 在技术上是视图的模型,因此任何未以某种方式显示的东西都不是视图模型。您描述的数据上下文听起来更像是存储库或服务——依赖注入就是为此而创建的。但是,是的,为了满足模式而创建 ViewModel 确实有效,但它可能会向您的应用程序添加不必要的代码(例如,没有必要为下拉列表创建一个只会成为键/值的查找数据类的视图模型绑定到下拉菜单)【参考方案4】:

您不应为模型对象创建 ViewModel。 一般来说,一个 ViewModel 应该属于一个 UserControl。它的作用是将您的视图(您的 XAML)与您的模型(业务逻辑)连接在一起。

在你的情况下,如果我理解正确的话,你有一堆实现业务逻辑的类(项目、装运等)。您的 ViewModel 将有权访问业务逻辑,并为您的视图提供要绑定的属性。

【讨论】:

因此,拥有一个包含形式列表的数据网格的视图模型(就像我目前拥有的那样)比拥有一个包含形式列表的项目视图模型更有意义。 @user1412240 这可能是语言障碍,但我不会说你有一个“为你的对象”的 ViewModel。 ViewModel 属于 View(您向用户显示的内容)。如果您想显示项目的某些属性,那么您的 ViewModel 必须有权访问项目对象。如果你只想显示 Proforma(我不知道这个词是什么意思),那么你最好在 ViewModel 中有一个 ObservableCollection 属性。 Marton,是的,这就是我的意思,profomas 只是在实际账单之前发送的用于销售商品的文件,它是“这是您打算购买的,以及我们打算多少向你收费”。问题来了, ObservableCollection 在我当前的设置中属于 Project 模型,因为形式属于一个项目,还是这是糟糕的设计? @user1412240 不,我认为该设计没有任何问题。我只是说,如果您只想显示(或更改)项目的一个属性(在本例中是其 Proformas 列表),那么您不需要在 ViewModel 中有一个 Project 对象。 对,我理解你的意思,但这给我留下了另一个问题,如果我的视图模型中没有项目对象,我将如何访问该列表,而不仅仅是列表。【参考方案5】:

我认为拥有包装模型数据对象的视图模型没有任何问题。视图模型不必是“每个视图一个”。它们可以表示列表中的一行或其他任何内容。

话虽如此,我很高兴直接绑定到模型对象,而且我经常这样做。我创建一个视图模型来包装它的唯一一次是如果我需要视图所需的每个对象的额外状态。

【讨论】:

看到就是这样,如果我要创建一个视图模型来包装数据对象,那只是为了满足模式,实际上没有用,因为 90%模型只显示在数据网格中 如果是这种情况,请不要这样做。数据项的包装不是必需的,但可以用于添加其他非实体属性,例如不会持久保存到数据存储的 selected row flagaggregated totals 不,根据我的第二段,只有在需要时才这样做。仅仅为了满足一些纯粹的 MVVM 模式而这样做是没有意义的。如果它可以为您提供所需的一切,则可以绑定到模型。

以上是关于WPF - MVVM 视图模型设置的主要内容,如果未能解决你的问题,请参考以下文章

WPF MVVM INotifyPropertyChanged 实现 - 模型或视图模型

多个视图模型之间状态共享的具体示例(WPF MVVM)

将 WPF ValidationRule 的状态传递给 MVVM 中的视图模型

WPF MVVM 对话框示例

WPF MVVM 将用户控件绑定到主窗口视图模型

如何从作为wpf mvvm模式中的窗口打开的视图模型中关闭用户控件?