【问题标题】:MVVM Design Hickups and grey areaMVVM 设计 Hickups 和灰色区域
【发布时间】:2015-08-29 10:55:51
【问题描述】:

在过去的几个月里,我一直在尝试熟悉 wpf 中的 mvvm 模式。这是一个被广泛讨论的话题,我遇到了一些相互矛盾的文章。我知道 mvvm 是一种模式,而不是普遍的真理,但是在某些情况下确实找到了决定什么去哪里,什么做什么的灰色区域。 99% 的时间是在 viewmodel 和 view 之间出现这些问题。

我目前决定什么属于哪里的心态:

模型:它只包含要保存到文件中的数据。不多也不少。

ViewModel:是视图和与该视图关联的一个或多个模型之间的通信桥梁。

视图:处理视觉表示、视觉弹出和视觉修改

第一个关于模型的简单问题: (案例 1)

到目前为止,在我看到的每个实现中,模型对象都是实现 INotifpropertyChanged 的​​类。为什么模型应该是一个类而不仅仅是一个描述数据应该是什么的接口并让视图模型实现这个接口?模型不应该处理 ui,即使它是解耦的,因此给模型类 INotifyPropertyChanged 与让 viewmodel 从 DependencyObject 继承一样错误(即使我理解它在该类型的模型实现中的重要性)?

ViewModel 与 View 应该处理的灰色区域示例: (案例 2)

假设我正在创建一个 Uml 编辑器,其中有节点和连接器在视图模型中创建并保存在他们自己的 ObservableCollection 中。

<SomePageView.DataContext>
    <vm:SomePageViewModel/>
</SomePageView.DataContext>
<Grid>
    <ItemsControl ItemsSource="{Binding Nodes}" DataTemplate="{Binding SomeNodeUserControl}"/>
    <ItemsControl ItemsSource="{Binding Connectors}" DataTemplate="{Binding SomeConnectorUserControl}"/>
</Grid>

当一个节点在网格周围拖动时,任何附加的连接器端点都将跟随该节点移动。我个人认为这是 UiElements 之间的通信,但是有两种完全不同的方式来实现这种行为。通过保持对 NodeviwModel 的完整引用来更新连接器,并使用其位置成员来更新连接器的位置。这是一种非常简单的方法,但不喜欢它,因为我认为它应该是更新 ConnectorVm 位置的 ConnectorView。这要求连接器视图了解特定的 NodeView。然而,这种方法更复杂,我还没有找到有效的解决方案(目前正在研究 EventAgregator 模式)。我喜欢最后一种方法的原因是,如果选择一个连接器,它应该是决定连接器如何移动的视图,而不是 ViewModel。对我的观察有什么建议或建议吗?

在 mvvm 模式中应该保留 ui 模式: (案例 3)

在 mvvm 中应该在哪里使用 eventAgregator、Mediator。在 ViewModel 或 CodeBehind 中?我目前的信念是它们属于背后的代码,但我对这件事有一点不确定性。如果保留在 codeBehind 中,我可以想象一种更简洁的方法,即使用带有加载事件的 VisualTree,而不是使用带有 Eventagregator 参数的构造函数等。 类似地,我可以实现一个不需要是单例的 UndoRedoController。应该更容易测试,可能有更多的 UndoRedoControllers 存在。一个示例可能是一个文档程序,它允许在选项卡中一次打​​开多个文档,因此应该有自己的 UndoController。

【问题讨论】:

  • 这是什么意思“当一个节点在网格周围拖动时,任何附加的连接器端点都会跟随该节点移动”。您可以添加屏幕截图或解释一下吗?
  • 模型还包含修改数据的方法。如果模型不提供任何更改通知,则视图模型必须定期检查以通知视图。这是不可取的。我还建议您添加一个示例应用程序并提出此示例说明的问题。通过这种方式,人们还可以检测出导致所需路径过时的设计缺陷。

标签: c# wpf design-patterns mvvm


【解决方案1】:

案例 1
在 M-V-VM 中,ViewModel 总是(Model not always)实现 INotifyPropertyChanged
模型可以但不是因为您真的不想使用 INPC 等相当 WPF 特定的功能来污染模型。

即使解耦,模型也不应该处理ui。

INPC 界面不是特定于 UI 的!它只是通知更改。实际上 WPF 大量使用它来识别更改,但这并不意味着它是一个 UI 界面。

案例 2
理想情况下,视图只负责数据的表示。虽然拖动是视图上的操作,但视图/ItemsControls 中的 UI/数据更新是基于视图模型的决定。 如果实现比在 ViewModel 中相对更容易和合乎逻辑,那么使用后面的代码并没有什么坏处,但是如果您可以在 viewmodel 中容纳相同的代码,那么您应该这样做。

案例 3
它应该在视图模型中。

如果保留在 codeBehind 我 csn 想象一个更清洁的方法,使用 带有加载事件而不是使用构造函数的 VisualTree 接受 Eventagregator 的参数,同样

使用事件会导致视图之间的紧密耦合,从而使应用程序更难维护。如果事件的主类(发布者)的寿命长于可视控件/用户控件(订阅者),并且您忘记或不知道何时取消订阅事件,它也可能导致内存泄漏。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-29
    相关资源
    最近更新 更多