【问题标题】:MVVM Design ConsiderationMVVM 设计考虑
【发布时间】:2011-06-20 18:38:00
【问题描述】:

我目前正在开发一个新的 WPF 应用程序,并且我的大部分业务逻辑层都已开发(即我的模型)。

我即将实现 ViewModel 类来代表我的应用程序的一项功能。我对 Model-View-ViewModel 模式很陌生,我有一个问题,即在实现我的 ViewModel 类时最好使用哪种方法。

从在线示例中,我发现模型通常是 ViewModel 的成员。使用这种方法,ViewModel 会公开 Model-member 的属性,以便它们可以绑定到 View 中的 Model。

例如:

Public Class MyViewModel
  Implements INotifyPropertyChanged

  Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

  Private _myModel As ModelClass

  Public Property MyModelPropertyA As Object
    Get
      Return _myModel.MyModelPropertyA
    End Get
    Set(ByVal value As Object)
      _myModel.MyModelPropertyA = value
      RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("MyModelPropertyA")
    End Set
  Public Property MyModelPropertyB As Object
    Get
      Return _myModel.MyModelPropertyB
    End Get
    Set(ByVal value As Object)
      _myModel.MyModelPropertyB = value
      RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("MyModelPropertyB")
    End Set
    '.... And so On'

End Class

我不喜欢这种方法的一点是,我将重写很多属性。

所以,我正在考虑在 ViewModel 中继承模型类而不是使用私有成员的选项。

像这样:

Public Class MyViewModel
      Inherits MyModel
      Implements INotifyPropertyChanged

      Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

      'Now all of my properties are inherited'
End Class

第二种方法的问题是我不确定如何在应用程序运行时将我的模型转换为视图模型。

您不能设置 viewModelInstance = ModelInstance。

(但你可以设置modelInstance = viewModelInstance)

我正在寻找有关如何实现 ViewModel 类的最佳方法的建议。

【问题讨论】:

  • 您对继承的使用很有趣。关于您的最后一个问题,您需要一个隐藏对象创建的工厂。不过,我看到的一个问题是,一个模型对象是否出现在两个不同的视图上,因此有两个不同的视图模型。如果它已经被实例化为 VmA,那么你不能变成 VmB。

标签: .net wpf vb.net mvvm viewmodel


【解决方案1】:

甚至不要考虑从模型继承你的 viewModel - 这将是一个没有人会喜欢的 hack。如果你太懒太暴露所有属性(顺便说一句 resharper 可以自动完成),那么你可以将你的模型包含到你的 viewModel 中,并通过一些只读属性提供对它的访问。但是您仍然应该在模型类中实现INotifyPropertyChanged

一些代码(对不起C#):

class Model : INotifyPropertyChanged
{
    public string Name { get; set; } // this raises PropertyChanged
}

class ViewModel
{
    private readonly Model _model;

    public Model Model { get { return _model; } }
}

查看 XAML:

<Textbox Text="{Binding Model.Name}" />

【讨论】:

  • 这不是偷懒的问题。这是一个紧迫的最后期限的问题。如果继承不是最好的方法,并且您认为它是“黑客”,请解释原因。
  • @Frinavale - 继承可能适用于您的情况,但我建议将模型添加为属性。我在这种继承中看到了几个缺点: a) 首先 - 对我来说,它完全混淆了 M 和 VM 之间的关系。就像基本的 OOP 书籍一样,Dog 从 Animal 继承,因为 Dog 是某种特定的动物,但没有一本说 Dog 应该从 Head 继承,因为它们都有鼻子、眼睛和耳朵。 b) 你的 VM 接收到你的模型所具有的功能(不确定你是否有),但假设你在 Model 中有一些持久化逻辑,你想持久化 VM 吗?
  • c) 您将无法(至少在没有多重继承的情况下)从其他东西继承您的 VM。我的虚拟机通常继承自 ViewModelBase 之类的东西,其中一些可能继承自 EntityEditorViewModelBase 之类的东西。您通过从 Model 继承来限制自己。 d) 您将无法在一行(!)代码中使用您的模型参数化您的 VM,而是必须复制所有属性(希望您不会忘记任何属性?)
  • 非常感谢您花时间向我解释这一点。我已经开始重做我的虚拟机以使用属性而不是继承。
  • Snowbear 推荐的方法首先写在(我认为)我在codeproject.com/KB/WPF/WPF-Presentation-Models.aspx 的文章中。但是 Frinavale 对继承的使用很有趣,至于 Snowbear 反对它的论点:(a)继承主要是关于接口继承,而不是关于狗是动物,以及(b)你的模型不应该包含持久性逻辑(这就是数据访问层是为了)。至于 (c) 中的限制,您可以随时这样做:msdn.microsoft.com/en-us/vcsharp/bb625996.aspx
【解决方案2】:

ViewModel 通常包装/封装与视图相关的逻辑。恕我直言,不需要使用 ViewModel 简单地传递模型数据。虽然纯粹的方法是定义一个 ViewModel 并传递这些数据;在某些情况下,这根本不需要,因为应用程序本质上是简单的。如果应用程序具有任何增长潜力,那么使用 ViewModel 将是必要的。

如果您有Person 模型; ViewModel 通常可能包含一个属性,该属性公开一个名为PeopleObservableCollection&lt;Person&gt;。 ViewModel 是 View 的编排器;不是模型的传递。

但是,由于上述原因,您不应该将您的模型与视图模型绑定,因为它们应该在理论和实践中相互解耦。

【讨论】:

  • 应用程序需要能够增长。但是,我发现模型中的属性之间存在一些复杂的关系,我发现仅使用转换器和视图很难准确地实现这些关系。我认为最好的方法是为我的模型创建视图模型。
  • @Frinavale "...将为我的模型创建视图模型..." 视图模型和模型不是 1:1。 ViewModel 与 View 的关系通常为 1:1。您不需要每个模型都有一个 ViewModel;创建一个 ViewModel,它可以负责为 View 准备不同的模型数据
【解决方案3】:

查看这张来自here的图表。

这是一个很好的参考图表,以确保您正确地遵循模式。如您所见,层之间有多种交互方式,但最主要的是分离。始终确保每一层只知道它的父层而不是子层,即 VM 知道模型,但不知道视图,模型知道业务层而不是视图模型或视图。

正如您从箭头中看到的(以及其他人提到的),模型可以“通过视图模型上的单个属性公开”,这意味着视图可以通过这个直接链接到模型,或者模型可以在 vm 上“在模型属性中抽象或重新实现”。

【讨论】:

  • 为了避免混淆,当我谈到“父层和它的子层”时,我的意思并不是分层的。我只是倾向于以分层的方式来考虑它们,比如模型在顶部,VM 在下面,然后在底部是视图。他们每个人都可以向上查找,但不能向下查找。
猜你喜欢
  • 1970-01-01
  • 2014-08-23
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-20
相关资源
最近更新 更多