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

发布于 2024-11-16 07:18:59 字数 2266 浏览 5 评论 0原文

在文章中,采用模型-视图-视图模型设计模式的 WPF 应用 ,作者乔什·史密斯说道:

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

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

编辑:我的问题是如下,为什么要尝试使用 AttachedCommandBehaviorInvokeCommandAction 等方法来避免代码隐藏编码。

让我解释一下更多 细节。

就(1)而言,我认为来自 AttachedCommandBehavior。由于 Border 没有为 MouseRightButtonDown 实现 ICommandSource,因此您通常无法绑定事件和 ICommand,但可以使用 < a href="http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/" rel="noreferrer">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 及其具有 Border_MouseRightButtonDown 方法的代码隐藏,该方法链接到上面提到的 (2) Josh Simth。

<Border MouseRightButtonDown ="Border_MouseRightButtonDown"/>

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

您对此有何看法?

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

(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.

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.)

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

Let me explain more detail.

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>

OR

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>

BUT,

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.

What do you think about this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

若无相欠,怎会相见 2024-11-23 07:18:59

为什么空代码隐藏被视为设计良好的MVVM

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

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

。 UI,即从使用 ListView 切换到 DataGrid,或者从使用标准 Microsoft 控件更改为使用其他供应商的控件。

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

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

但要记住的一件事是 MVVM 是一种模式,而模式只是一种配方或处方 用于在特定情况下取得特定结果。它不应该被视为一种宗教,非信徒或不遵守宗教信仰的人会去某个炼狱(尽管如果你想避免维护地狱代码气味)。

如果您想要一个很好的示例来说明此特定模式如何提供帮助,请尝试在 ASP.Net 中编写一些相当复杂的屏幕,然后在 WPF 或 Silverlight 中编写相同的屏幕,并注意其中的差异。


编辑:

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

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

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

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

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

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

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:

  • the viewmodel (and model) is easier to test in isolation
  • you have achieved a good level of loose coupling, which has excellent benefits from a maintenance and extensibility perspective

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.

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.

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).

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.


Edit:

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

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

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)

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.

茶花眉 2024-11-23 07:18:59

MVVM可以将代码和页面设计完全分离;程序员只关心编码,设计师只关心设计。但是:

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

The MVVM can split code and page design completely; coders just care about coding and designers only care about design. But:

  1. I've never seen any designer who using Blend or understanding XAML.
  2. Almost all XAMLs are written by coder himself.
夜无邪 2024-11-23 07:18:59

代码隐藏本身并没有什么坏处。对于简单的情况,拥有它就可以了。然而,在许多情况下,UI 逻辑可能会变得难以管理。将逻辑封装在附加行为和视图模型中使我们能够隔离变量(并测试它们),以便更容易理解和维护。

如果可测试性是一个问题,那么您可以在视图模型和附加行为中封装的 UI 逻辑越多,您就越能够在不诉诸 UI 测试的情况下进行验证。 (虽然它并不能完全消除 UI 测试的需要,但它确实在进行 UI 测试之前提供了第一级验证,这将更加耗费时间/资源。

There is nothing inherently bad in code-behind. For simple cases, it's fine to have it. However, UI logic can get difficult to manage in many scenarios. Encapsulating that logic in attached behaviors and view models allows us to isolate the variables (and test them) so that it's easier to comprehend and maintain.

If testability is a concern, the more of your UI logic that you can encapsulate in viewmodels and attached behaviors, the more you you will be able to verify without resorting to UI testing. (While it doesn't eliminate the need for UI Testing altogether, it does provide a first level of verification before engaging in UI Testing which will be more time/resource intensive.

筱武穆 2024-11-23 07:18:59

我认为引用的部分指的是数据可视化的方式。我认为他们的意思是您不应该在代码后面编写代码,例如与数据显示方式或位置相关的代码(例如:label1.Text = ...)。使用绑定执行类似的操作可以更轻松地分离设计和代码(如果您需要在更高版本中将数据显示在名为“tbTest”的文本框中,会发生什么?您必须更改后面的代码)。

他们并不是说你不应该在代码后面有任何代码 - 他们只是说在理想的世界中,你只会对事件做出反应或处理无法处理的数据。

至少这是我从你引用的部分中了解到的。

I think the quoted section refers to the way data is visualized. I think they mean you should not write code in code behind that is, for example, related to how or where the data is displayed (for example something like: label1.Text = ...). Doing things like that using bindings makes it easier to separate design and code (what happens if you need the data to be displayed in a text box named "tbTest" in a later version? You'd have to change your code behind).

They are not saying that you shouldn't have any code in code behind - they are just saying that in an ideal world, you'd only react to events or process data that could not otherwise be processed.

At least that's what I understand from the section you quoted.

万劫不复 2024-11-23 07:18:59

MVVM 模式很强大,但我发现它太“纯粹”了。我可以看到好处是让后台代码处理视图中的所有命令和属性,而 ViewModel 则关注对业务模型属性的任何转换。
这样做的好处是,如果您希望更改用户 UI,也许从桌面更改为浏览器,则可能只是替换视图及其背后的代码。

只是我的想法!!

The MVVM pattern is powerful but I find it too 'Purist'. I can see benefit is having the code behind handle all the commands and properties in the view while the ViewModel is concerned with any translation to business model properties.
A benefit of this is that if you wish to change the user UI, perhaps from desktop to browser it is likely to be just replacing the View and its code behind.

Just my thoughts!!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文