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

Jin*_*ung 47 xaml code-behind mvvm

在文章中,使用Model-View-ViewModel设计模式的WPF应用程序,Josh Smith的作者说:

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

我的问题是,在(1),为什么空代码被认为是一个设计良好的MVVM.(听起来空的代码隐藏总是很好.)

编辑:我的问题是,如下,为什么像的方法AttachedCommandBehavior或者InvokeCommandAction是试图避免代码隐藏编码.

让我解释一下细节.

就(1)而言,我会认为以下情况来自AttachedCommandBehavior.由于Border没有实现ICommandSourcefor MouseRightButtonDown,你不能通常绑定事件和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>
Run Code Online (Sandbox Code Playgroud)

要么

我们可以这样做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>
Run Code Online (Sandbox Code Playgroud)

但,

我们使用以下XAML及其代码隐藏Border_MouseRightButtonDown方法,该方法与上述(2)Josh Simth相关联.

<Border MouseRightButtonDown ="Border_MouseRightButtonDown"/>
Run Code Online (Sandbox Code Playgroud)

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

你怎么看待这件事?

slu*_*ter 68

为什么空代码被视为精心设计的MVVM

拥有一个代码隐藏文件,它只包含在构造函数中调用InitializeComponent(),这意味着你已经达到了纯度 - 你的代码隐藏绝对没有逻辑.您没有使用视图模型或模型中正确属于的任何代码污染您的视图.这意味着一些事情:

  • viewmodel(和model)更容易单独测试
  • 你已经达到了良好的松耦合水平,从维护和可扩展性的角度来看,它具有很好的优势

当您必须更改UI时,即从使用ListView切换到DataGrid,或者从使用标准Microsoft控件更改为使用其他某些供应商时,这些好处确实变得显而易见.

如上所述,有时候在代码隐藏文件中避免使用一些代码是不可能的.你应该确保的是你所拥有的代码纯粹是与UI相关的.例如,如果您有ComboA和ComboB,并且ComboB是为了响应ComboA中的选择而设置的,那么从视图中设置ComboB的SelectedIndex就可以了,但是设置ComboB的Items或SelectedItem不是 - 那些属性是两个数据相关,应通过绑定到viewmodel来指定.SelectedIndex属性直接与视觉相关,并且在某种程度上与实际数据无关(并且与viewmodel无关).

如果您确实从视图中的代码隐藏访问viewmodel,您应该尝试通过接口进行操作.这意味着您的viewmodel作为接口注入或提供给视图.(请注意,绑定子系统不知道或不关心接口,它将继续以正常方式绑定.这实现了更好的代码,耦合更少紧密).我编写它的方式,viewmodel不知道视图存在,并且视图只知道viewmodel作为接口.

但要记住的一件事是MVVM是一种模式,而模式只是在某种情况下实现某种结果的一种方法或处方.它不应该被视为一种宗教,非信徒或非信仰者会去一些炼狱(虽然如果你想避免维护地狱代码气味的炼狱,坚持这种模式是好的).

如果你想要一个关于这个特定模式如何帮助的一个很好的例子,尝试在ASP.Net中编写一些相当复杂的屏幕,然后在WPF或Silverlight中编写相同的内容,并注意区别.


编辑:

让我回答你的一些问题,我希望它有所帮助....

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

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

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

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

  • @Aaron - 别误会我的意思.只有原教旨主义者要求在视图后面的代码中应该没有代码(注意我称之为*原教旨主义*,而不是*纯粹主义者*).我完全支持代码中的视图/ UI相关代码,因为我经常看到代码鞋已经进入虚拟机,因为人们认为他们必须这样做.有时候代码应该去哪里是一个艰难的决定,有时代码将在后面的代码和VM之间分开. (2认同)
  • @Slugster:我很高兴还有其他人和我一样,我开始认为我是唯一的人!干杯:) (2认同)

小智 8

MVVM可以完全分割代码和页面设计; 编码员只关心编码而设计师只关心设计.但:

  1. 我从未见过任何使用Blend或了解XAML的设计师.
  2. 几乎所有的XAML都是由编码员自己编写的.


Mic*_*own 5

代码隐藏没有本质上的坏处。对于简单的情况,拥有它很好。但是,在许多情况下,UI逻辑可能变得难以管理。将该逻辑封装在附加的行为和视图模型中,使我们能够隔离变量(并对其进行测试),从而更易于理解和维护。

如果需要考虑可测试性,则可以在视图模型和附加行为中封装的UI逻辑越多,无需借助UI测试就可以验证的逻辑越多。(虽然它并不能完全消除对UI测试的需求,但它确实提供了进行UI测试之前的第一级验证,这将花费更多的时间/资源。