【问题标题】:MVVM: Should a VM object expose an M object directly, or only through getters delegating to M's getters?MVVM:VM 对象应该直接公开 M 对象,还是仅通过委托给 M 的 getter 的 getter?
【发布时间】:2010-11-10 00:23:58
【问题描述】:

最好的解释方式是这样:

这是模型

public class Person 
{
    public int age;
    public string name;
}

这是视图模型

public class PersonVM
{    
}

我的问题是:
虚拟机应该将人暴露给数据模板还是用他自己的属性封装模型属性?

【问题讨论】:

  • 在这些简单的场景中,拥有视图模型的好处为零。如果您说想要实现撤消功能等,那么该逻辑将进入 VM,您将包装模型属性。

标签: c# design-patterns mvvm getter access-modifiers


【解决方案1】:

没有关于这个问题的普遍共识。例如,这是 Ward Bell here 提出的关于 MVVM 的开放式问题之一:

是否允许 VM 提供 V an 展开的 M 对象(例如,原始 员工) ?或者必须是 M 对象的 属性(如果它甚至被允许 有属性!)被暴露 完全通过一个表面 虚拟机包装器?

不直接在 VM 中公开模型的主要优点是:

  • 您可以将其用作“类固醇转换器”,以方便的方式为视图格式化模型值

  • 您可以注入其他与用户界面相关的功能,例如data validation messagesundo redo、..

缺点是:

  • 您必须复制大量代码才能在视图模型中公开所有模型属性。

  • 如果您将视图控件绑定到 viewmodels 属性,您将从 viewmodel 发送 propertyChanged 事件。但是,如果模型属性从不同于 viewmodel 设置器的其他来源更改会发生什么?然后它必须通知视图模型,所以你以 2 个 OnPropertyChanged 结束,一个在模型中,一个在视图模型中……相当复杂!

所以对我来说正确的答案是:这取决于您的要求。

【讨论】:

  • 有一个简单的解决方案,让视图模型成为代理,包含您想要添加的所有功能
  • 这是一个很好的例子,展示了“包装”模型然后在顶部注入更多功能的好时机。
  • @ChenKinnrot - 实际上,“缺点”“使视图模型成为代理”所需的编码的描述,因为当任何属性发生变化时,视图模型必须通知视图。将代码“注入”到属性中的库可以提供帮助,例如 Fody/PropertyChanged。如果修改了 Model 对象的属性,那么痛苦就来了,那么必须告知该 M 对象的任何 VM(S)哪个 M 属性发生了更改,以便它们可以触发其相应的 OnPropertyChanged,以告知数据绑定的 V。
【解决方案2】:

Robert McCarter 在 MSDN 第 25 卷中提出了一个有趣的解决方案。

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

他使用动态视图模型在模型之上提供一个层,同时避免代理所有模型属性。

如果您的问题空间不需要高性能(动态确实会影响性能),这是一个很好的解决方案。 View 不需要知道有关 Model 的任何信息,但 View Model 不必代理“按原样”提供的属性。可以随时将属性添加到 View Model 以包装 Model 属性,而无需修改 View 或 Model。阅读文章了解更多详情。

【讨论】:

  • 或者使用两全其美... 定义一个动态基础视图模型类和一个非动态基础视图模型。对高性能视图模型使用非动态,对其他一切使用动态基类。
  • 记住 Silverlight 不支持 DLR :'( 我也讨厌代理属性。
  • @Shimmy:这并不奇怪。 Silverlight 仍然缺少 WPF 的许多优点。
【解决方案3】:

拥有任何模型的 ViewModel 可能比这更糟糕。如果你有一个模型的层次结构,甚至是一个简单的集合怎么办? 在这种情况下,您必须遍历所有模型并为每个模型构建一个 ViewModel 实例,并且还必须注册通知更改事件或其他事件。恕我直言,这完全是疯狂和不合理的。正如 DaniCE 所说,您最终会遇到大量代码和头疼的问题。

【讨论】:

  • 我同意你的看法,现在怎么样了?
  • 将现有模型树包装到 VM 中是一个相对简单的过程。创建一个实用程序来执行此操作,或者更好地将其构建到您自己的基础 VM 类中。是的,设置虚拟机并将它们与视图耦合会增加一些复杂性,但这是值得的。结果是可视化树的每个级别的智能代码元素。无需再搜索可视化树来寻找处理操作的人员。
  • 在没有视觉命中测试的情况下,我从来没有找到一种处理单个 ListBoxItems 复杂操作的好方法......直到 MVVM。一旦您构建了 VM 层次结构并为每个 VM 提供了正确的引用,ListBoxItems 就拥有了执行自己的复杂操作所需的一切。
  • 如果您不将模型与视图分开,为什么需要 viemmodel?您最终可能会将模型设置为数据上下文。
  • @IgnacioSolerGarcia - 确实可以。请注意,在实践中,这意味着将 ViewModel 中的所有内容添加到您的模型中。例如。从 System.Component.INotifyPropertyChanged 继承并在每个公共属性的设置器上触发 OnPropertyChanged。 (或者从任何 MVVM 库中的适当 VM 基类继承,并根据 MVVM 库的需要标记这些属性。)所以对我来说,实际上问题变成了 “你需要一个 模型?"(总是需要一个视图模型)。
【解决方案4】:

视图模型应该声明它自己的属性并在视图中隐藏模型的细节。这为您提供了最大的灵活性,并有助于防止视图模型类型问题泄漏到模型类中。通常,您的视图模型类通过委托封装模型。例如,

class PersonModel {
    public string Name { get; set; }
}

class PersonViewModel {
    private PersonModel Person { get; set;}
    public string Name { get { return this.Person.Name; } }
    public bool IsSelected { get; set; } // example of state exposed by view model

    public PersonViewModel(PersonModel person) {
        this.Person = person;
    }
}

请记住:模型不应该知道任何关于正在使用它的视图模型的信息,并且视图模型也不应该知道任何关于正在使用它的视图的信息。视图应该对潜伏在后台的模型一无所知。因此,将模型封装在视图模型中的属性后面。

【讨论】:

  • PersonViewModel 类有一个带有 PersonModel 参数的公共构造函数。所以视图必须知道模型才能实例化视图模型……不是mvvm。构造函数应该是内部的,或者只接受像字符串这样的通用参数来实例化模型。
  • 假设您更改模型中的值(通过 UI)。因此,模型随后也会更改一些其他值(因为它们依赖于它)。然后你会触发 OnPropertyChangedHandler 并让 VM 更新它自己的属性吗?这是我所做的并且效果很好,但我是新手,不确定它是否是不好的做法。
  • 回复上述评论。这篇文章似乎完全实现了我所做的。 msdn.microsoft.com/en-us/magazine/ff798279.aspx
猜你喜欢
  • 1970-01-01
  • 2012-12-18
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 2013-08-18
  • 1970-01-01
  • 2017-11-11
  • 1970-01-01
相关资源
最近更新 更多