MVVM设计Hickup和灰色区域

时间:2015-08-29 10:55:52

标签: c# wpf design-patterns mvvm

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

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

模型:它只包含要保存到文件的数据。没什么,没什么。

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

视图:处理可视化表示,可视化弹出窗口和视觉修改

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

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

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

假设我正在创建一个Uml编辑器,其中有一个节点和连接器在viewModel中创建并保存在自己的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的完整引用来更新连接器,并使用其位置成员来更新连接器的位置。这是一个非常简单的方法,但不喜欢它,因为我认为应该是ConnectorView更新ConnectorVm的位置。这要求Connector View了解有关特定NodeView的信息。然而,这种方法更复杂,我还没有找到一个有效的解决方案(目前正在调查EventAgregator模式)。我喜欢最后一个方法的原因是,如果选择一个连接器,它应该是指示如何移动连接器而不是ViewModel的视图。对我的观察有任何意见或建议吗?

mvvm模式中的

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

在mvvm中应该使用eventAgregator,Mediator。在ViewModel或CodeBehind中?我目前的信念是他们属于背后的代码,但我对此事有一丝不确定性。如果保存在codeBehind中,我可以通过使用带有loadedevent的VisualTree而不是使用带有Eventagregator参数的构造函数来设想更清晰的方法。 类似我可以实现一个UndoRedoController,它不需要是一个单例。应该更容易测试,可能有更多的UndoRedoControllers存在。一个示例可以是一个文档程序,允许在选项卡中一次打​​开多个文档,因此应该有自己的UndoController。

1 个答案:

答案 0 :(得分:1)

案例1
在M-V-VM中,ViewModel始终(Model not always)实现INotifyPropertyChanged
模型可以但不是因为你真的不想用像INPC这样的WPF特定功能来污染模型。

  

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

INPC界面不是特定于UI的!它只是告知变化。事实上,WPF大量使用它来识别变化,但并不意味着它是一个UI界面。

案例2
View理想地仅用于表示数据。虽然拖动是对视图的操作,但视图/ ItemsControls中的UI /数据更新基于viewmodel的决策。 如果实现比ViewModel中的实现相对容易和逻辑,那么使用后面的代码没有任何害处,但是如果你可以在viewmodel中容纳相同的代码,那么你应该这样做。

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

  

如果保存在codeBehind中,我可以想象一下使用的更清洁的方法   VisualTree与loadedevent而不是使用构造函数   采用Eventagregator的论据同样

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