为什么要避免WPF MVVM模式中的代码隐藏?

在这篇文章中, WPF应用程序使用Model-View-ViewModeldevise模式 ,作者Josh Smith说:

(1)在devise良好的MVVM体系结构中,大多数视图的代码隐藏应该是空的,或者最多只包含操作该视图中包含的控件和资源的代码。 (2)有时也需要在视图的代码隐藏中编写代码,与ViewModel对象进行交互,比如挂钩一个事件或调用一个本来很难从ViewModel调用的方法。

我的问题是,在(1)中,为什么空的代码隐藏被认为是一个精心devise的MVVM。 (这听起来是空的代码总是好的。)

编辑:我的问题是,如下所示,为什么像AttachedCommandBehaviorInvokeCommandAction方法试图避免代码隐藏编码。

让我解释更多的细节。

就(1)而言,我认为就像AttachedCommandBehavior一样 。 由于边界没有为MouseRightButtonDown实现ICommandSource ,所以不能共同绑定事件和ICommand ,但可以使用AttachedCommandBehavior 。

 <!-- I modified some code from the AttachedCommandBehavior to show more simply --> <Border> <local:CommandBehaviorCollection.Behaviors> <local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/> </local:CommandBehaviorCollection.Behaviors> </Border> 

要么

我们可以用System.Windows.Interactivity.InvokeCommandAction来做到这一点。

 <Border xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" > <i:Interaction.Triggers> <i:EventTrigger EventName="MouseRightButtonDown"> <i:InvokeCommandAction Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/> </i:EventTrigger> </i:Interaction.Triggers> </Border> 

但,

我们使用下面的XAML及其代码隐藏方法,该方法与上面(2)Josh Simth链接的Border_MouseRightButtonDown方法相关。

 <Border MouseRightButtonDown ="Border_MouseRightButtonDown"/> 

我认为使用上面的代码隐藏并不坏,只是因为它们之间的区别只在绑定命令或添加事件处理程序的地方。

你怎么看待这件事?

为什么空的代码隐藏被视为一个精心devise的MVVM

有一个代码隐藏文件,只包含在构造函数中对InitializeComponent()的调用,意味着你已经达到了纯粹性 – 在代码隐藏中你完全没有逻辑。 您并没有使用视图模型或模型中的任何代码来污染您的视图。 这意味着一些事情:

  • 视图模型(和模型)更容易孤立地进行testing
  • 你已经达到了一个很好的松耦合的水平,从维护和可扩展性的angular度来看,这是非常有利的

如果您必须更改UI,即从使用ListView切换到DataGrid,或者从使用标准Microsoft控件更改为使用其他某个供应商,则好处将变得十分明显。

如前所述,在代码隐藏文件中有时候不可能避免使用一些代码。 你应该确保的是,你所拥有的代码纯粹是与UI相关的。 举个例子,如果你有ComboA和ComboB,并且ComboB被设置为响应ComboA中的select,那么从视图中设置ComboB的SelectedIndex是好的,但是设置ComboB的Items或者SelectedItem不是 – 那些属性是这两个数据相关,应通过绑定到视图模型来指定。 SelectedIndex属性直接与视觉相关,并且与实际数据有些独立(并且与视图模型无关)。

如果您在视图中从代码隐藏中访问视图模型,则应尝试通过界面进行操作。 这意味着你的viewmodel被注入或者作为一个接口被赋予视图。 (请注意,绑定子系统不知道或不关心接口,它将继续以正常的方式绑定。这样做的代码更好,耦合也更less)。 我编码的方式,viewmodel不知道存在一个视图,视图只知道作为一个接口视图模型。

有一点要记住,MVVM是一种模式,一种模式只是在某种情况下达到一定结果的配方或处方 。 它不应该被视为一个宗教,非信徒或非异教徒要去炼狱(虽然如果你想避免维护地狱和代码味道 purgatory遵守模式是好的)。

如果你想要一个很好的例子来说明这个特定模式如何起作用,可以尝试在ASP.Net中编写一些相当复杂的屏幕,然后在WPF或Silverlight中编写相同的屏幕,并注意区别。


编辑:

让我回答你的一些问题,我希望它可以帮助….

在我看来,视图模型(视图模型)angular色具有UI逻辑和视图状态

视图模型不应该有任何UI逻辑或“视图状态”。 为了这个解释的目的,我将视图状态定义为滚动位置,select的行索引,select的索引,窗口大小等等。这些都不属于视图模型; 像SelectedIndex这样的东西是特定于在UI中显示数据的方式(如果更改DataGrid的sorting顺序,则SelectedIndex可以更改,即使SelectedItem仍然相同)。 在这种情况下,SelectedItem可以绑定到视图模型,但是SelectedIndex不应该。
如果你需要跟踪UI会话types信息,那么你应该想出一些通用的(例如,我通过保存重要的东西到KeyValuePair列表之前坚持视图状态),然后通过调用viewmodel(通过前面提到的接口)。 该视图不知道如何保存数据,视图模型不知道数据是从一个视图(它只是通过其接口暴露一个调用)。

视图的作用是显示一些内容并同步视图模型(具有数据绑定代码)

是的,视图的责任仅仅是直观地显示由视图模型呈现的数据。 视图模型从模型中获取数据(模型负责进行数据库调用或WCF web服务调用,这通常是通过一个“服务”来完成的,但是这是另外一个讨论)。 视图模型然后可以形成或操纵数据,即它可以获得所有客户的列表,但是只在该视图可以绑定到的公共属性中公开该列表的过滤版本(可能是当前的客户)。
如果要将数据操作为可视化(一个常见的示例是将枚举值转换为颜色),则视图模型仍然只有枚举值,并且视图仍然绑定到该值,但视图还使用转换器将纯数据转换为可视化表示。 通过使用转换器,视图模型仍然避免了与UI有关的任何事情,并且视图避免了任何真实的逻辑。

代码隐藏本身并没有什么坏处。 对于简单的情况,它是好的。 但是,在许多情况下,UI逻辑可能难以pipe理。 将该逻辑封装在附加的行为和视图模型中,可以让我们隔离variables(并testing它们),以便更易于理解和维护。

如果可testing性是一个问题,那么您可以将更多的UI逻辑封装在视图模型和附加的行为中,那么您无需借助UItesting就可以validation的内容越多。 (尽pipe它不会完全消除对UItesting的需求,但它在进行UItesting之前提供了第一级validation,这将会花费更多的时间/资源。

MVVM可以完全分割代码和页面devise; 编码人员只关心编码,devise师只关心devise。 但:

  1. 我从来没有见过任何使用Blend或理解XAML的devise师。
  2. 几乎所有的XAML都是由编码人员自己编写的。

我认为引用的部分是指数据的可视化方式。 我认为这意味着你不应该在后面的代码中编写代码,例如,与数据如何或在何处显示有关(例如: label1.Text = ... )。 使用绑定来做这样的事情可以更容易地将devise和代码分开(如果需要将数据显示在以后版本的“tbTest”文本框中,会发生什么情况?您将不得不更改代码)。

他们并不是说你不应该在代码背后有任何代码 – 他们只是说,在一个理想的世界里,你只能对事件或处理不能被处理的数据作出反应。

至less这是我从你引用的部分所了解的。