为什么要避免 WPF MVVM 模式中的代码隐藏? [英] Why to avoid the codebehind in WPF MVVM pattern?

查看:24
本文介绍了为什么要避免 WPF MVVM 模式中的代码隐藏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在文章中,采用模型-视图-视图模型设计模式的 WPF 应用程序,作者是 Josh Smith 说:

At the article, WPF Apps With The Model-View-ViewModel Design Pattern, the author who is Josh Smith said:

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

(1) In a well-designed MVVM architecture, the codebehind for most Views should be empty, or, at most, only contain code that manipulates the controls and resources contained within that view. (2) Sometimes it is also necessary to write code in a View's codebehind that interacts with a ViewModel object, such as hooking an event or calling a method that would otherwise be very difficult to invoke from the ViewModel itself.

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

My question is ,at the (1), why the empty codebehind is regarded as a a well-designed MVVM.(It sounds that the empty codebehind is always good.)

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

My question is, as the following, why the approach like the AttachedCommandBehavior or the InvokeCommandAction is tried to avoid the codebehind coding.

让我解释更多细节.

就 (1) 而言,我认为以下情况来自 AttachedCommandBehavior.由于 Border 没有为 MouseRightButtonDown 实现 ICommandSource,您通常不能绑定事件和 ICommand,但可以使用 AttachedCommandBehavior.

As far as the (1) is concerned, I would think like the following situation as from the AttachedCommandBehavior. As the Border doesn't implement the ICommandSource for the MouseRightButtonDown, you cannot commonly bind the event and the ICommand, but can do with the 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 做到这一点.

We can do this with the 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 及其具有 Border_MouseRightButtonDown 方法的代码隐藏,该方法链接到上述 (2) Josh Simth.

We use the following XAML and its codebehind having the Border_MouseRightButtonDown method, which is linked to the (2) Josh Simth said above.

<Border MouseRightButtonDown ="Border_MouseRightButtonDown"/>

我认为使用上面的代码隐藏还不错,因为它们之间的区别仅在于绑定命令或添加事件处理程序的位置.

I think using the codebehind as above is not bad just because the difference between these is only where binding a command or add event handler is.

你怎么看这个?

推荐答案

为什么空代码隐藏被认为是一个设计良好的MVVM

why the empty codebehind is regarded as a a well-designed MVVM

拥有一个仅包含在其构造函数中调用 InitializeComponent() 的代码隐藏文件意味着您已经实现了纯度 - 您的代码隐藏中的逻辑绝对为零.您没有使用任何正确属于视图模型或模型的代码污染您的视图.这意味着几件事:

Having a code-behind file which consists solely of a call to InitializeComponent() in its constructor means you have achieved purity - you have absolutely zero logic in your codebehind. You have not polluted your view with any code that rightfully belongs in the viewmodel or model. This means a couple of things:

  • 视图模型(和模型)更容易单独测试
  • 您已经实现了良好的松散耦合,从维护和可扩展性的角度来看,这具有极好的优势

当您必须更改 UI 时,好处会变得非常明显,即您从使用 ListView 切换到 DataGrid,或者从使用标准 Microsoft 控件更改为使用其他供应商的控件.

The benefits really become noticeable when you have to change your UI, i.e. you switch from using a ListView to a DataGrid, or you change from using the standard Microsoft controls to using some other vendor's.

尽管如此,有时无法避免代码隐藏文件中的少量代码.您应该确保您拥有的代码完全与 UI 相关.例如,如果您有 ComboA 和 ComboB,并且 ComboB 是根据 ComboA 中的选择设置的,那么从视图中设置 ComboB 的 SelectedIndex 就可以了,但是设置 ComboB 的 Items 或 SelectedItem 则不是 - 这些属性是两个数据都相关并且应该通过绑定到视图模型来指定.SelectedIndex 属性与视觉直接相关,并且在某种程度上独立于实际数据(并且与视图模型无关).

As mentioned though, it is sometimes impossible to avoid a little code in the code-behind file. What you should ensure is that the code you do have is purely UI related. As an example, if you have ComboA and ComboB, and ComboB is set in response to the selection in ComboA, then setting the SelectedIndex of ComboB from the view is fine, but setting the Items or the SelectedItem of ComboB is not - those properties are both data related and should be specified via binding to the viewmodel. The SelectedIndex property is directly visual related and somewhat independent of the actual data (and it is irrelevant to the viewmodel).

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

If you do access the viewmodel from code-behind in the view, you should try and do it via an interface. This means your viewmodel is injected or given to the view as an interface. (Note that the binding subsystem doesn't know or care about the interface, it will continue to bind in its normal way. What this achieves is better code, with less tight coupling). The way I code it, the viewmodel has no idea that a view exists, and the view only knows about the viewmodel as an interface.

但要记住的一件事是 MVVM 是一种模式,而模式只是一个 在某种情况下取​​得某种结果的方法或处方.它不应该被视为一种宗教,在那里非信徒或不遵守规则的人将进入炼狱(尽管如果您想避免炼狱,那么遵守该模式是好的维护地狱代码异味).

One thing to remember though is that MVVM is a pattern, and a pattern is simply a recipe or prescription for achieving a certain result in a certain situation. It shouldn't be treated as a religion, where non-believers or non-conformers are going to go to some purgatory (although adherence to the pattern is good if you want to avoid the purgatory of maintenance hell and code smell).

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

If you want an excellent example of how this particular pattern helps, try writing a few reasonably complicated screens in ASP.Net, and then write the same in WPF or Silverlight, and note the difference.

让我回答你的一些问题,希望能帮到你....

let me answer some of your questions, I hope it helps....

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

the viewmodel's (model of view) role , in my view, has UI logic and state of a view

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

The viewmodel should never have any UI logic or "view state" in it. For the purposes of this explanation, I would define view state as scroll position, selected row index, selected index, window size, etc. None of those belong in the viewmodel; things like SelectedIndex are specific to the way the data is shown in the UI (if you change the sort order of a DataGrid then the SelectedIndex can change, even though the SelectedItem is still the same). In this particular case, the SelectedItem can be bound to the viewmodel, but the SelectedIndex shouldn't.
If you need to keep track of UI session type info them then you should come up with something generic (for example, I have persisted view state before by saving important stuff into a KeyValuePair list) which is then "saved" with a call to the viewmodel (via the interface I mentioned previously). The view has no idea how the data is being saved, and the viewmodel has no idea the data is coming from a view (it has simply exposed a call through its interface).

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

and the view's role is displaying some contents and synchronizing the viewmodel(having databinding code)

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

Yes, the view's responsibility is simply to visually display data presented by the viewmodel. The viewmodel gets the data from the model (the model is responsible for making database calls or WCF webservice calls, this will usually be done via a "service", but that is a whole other discussion). The viewmodel can then shape or manipulate the data, i.e. it may get a list of all customers, but only expose a filtered version of that list (maybe the current customers) in a public property which the view can then bind to.
If the data is to be manipulated into something visual (a common example is an enum value being translated into a color), then the viewmodel still only has the enum value(s), and the view still binds to that value, but the view also uses a converter to translate the pure data to a visual representation. By using the converter the viewmodel has still avoided doing anything UI related, and the view has avoided any real logic.

这篇关于为什么要避免 WPF MVVM 模式中的代码隐藏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆