MVVM ICommand替代方案

4
我已经开始创建一个WPF MVVM应用程序。ViewModel中一个至关重要的组成部分似乎是一堆ICommands,以实现视图与ViewModel松耦合的交互方式。
我的问题是,为什么我不能直接绑定到一个方法?
我使用了Josh Smith的RelayCommand ICommand实现,它允许您将委托注入到一个ICommand对象中,但是,是否有更简单的方法允许按钮推送调用ViewModel中的方法?
我对MVVM很新,我相信我需要一些启示。

1
你可以绑定到一个方法:<Button Click="YouEventHandlerMethod"/>... 但是你可能想要将启用/禁用逻辑和执行逻辑合并在一起吗?这就是为什么你需要命令。 - Lex Lavnikov
5个回答

7
你无法直接将方法绑定到Button(例如)中,因为其没有接受委托的属性。相反,它具有类型为ICommandCommand属性。 RelayCommand(又名DelegateCommand)只是包装委托的ICommand
我认为从技术上讲,视图通过标记扩展绑定到视图模型中的特定方法是完全可能的。
<Button Command="{ViewModelMethod SomeMethodName}"/>

然而,这样做会更慢,并增加视图和视图模型之间的耦合。如果视图只知道类型为ICommand的视图模型上的属性,则该命令的实现可能完全改变(或方法名称可能更改)而视图不知情。


为什么会很慢?它需要使用反射吗?但是我确实理解你提到的耦合问题。 - Jose
其实,我不认为它会慢多少,因为绑定到ViewModel命令也使用反射。然而,这种方法有一个缺点:由于ViewModelCommand标记扩展不是绑定,所以当ViewModel更改时它不会被更新:它只会被评估一次(除非它挂钩到DataContextChanged事件进行重新评估,但那是另一回事...)。 - Thomas Levesque
首先,我说的是“慢一点”,而不是“慢”。这是一个重要的区别。其次,我谈论的是要调用的方法的分辨率和调用方式,而不是绑定本身。使用虚拟机创建的委托将比从视图中反射解析方法更快。因此,在所有其他缺点之上,从视图中执行会更慢。 - Kent Boogaart

6

我完全不同意。

调用速度并不重要:命令是用户交互,它们从不需要速度。

关于耦合的论点也是有缺陷的。为什么 {Binding MyProperty} 不是耦合,而 {ViewMethod MyMethod} 是呢?

需要专门制作“命令”来包装方法的要求很愚蠢。命令可能是有用的实现方式,但我们已经在C#中拥有了方法,用庞大笨重的东西替换它们是不正确的。

那个关于MarkupExtension和Binding的事情确实很困难。但它可以做到。实际上,它已经做到了,您可以查看CodePlex上的MethodCall项目:http://methodcallthing.codeplex.com/

您可以使用绑定来选择方法的“this”,并可以使用绑定来获取参数。所有这些都是实时的,即在调用命令时计算。另一个奖励功能是方法调用的推出结果,您也可以使用绑定进行此操作(OneWayToSource)。


1
+1 纠正 Kent Boogaart 回答中的缺陷。您的解决方案很有趣,但它没有意识到 ICommand 是一种很好的将 Execute 和 CanExecute 实现配对的方式。 - HappyNomad

5

ICommand提供了CanExecute,这对于控制启用非常重要。而简单的委托则不然。使用ICommand是明智之选。


3
显然,微软需要一个命令成为一流的东西,可能是因为他们觉得CanExecute对大多数应用程序都是必要的。我不同意,并认为CanExecute只应该是另一个DependencyProperty,你可以将它绑定到viewmodel的属性上,但是,嘿,我知道什么呢?
也许他们还认为有必要将命令的实现与控件的数据上下文分离。但是,我认为这似乎是不必要的,因为逻辑应该靠近正在操作的数据,这是面向对象的基本原则之一。
个人而言,我避免在MVVM中使用命令,因为它们实现起来很麻烦。我只是让视图的代码后台将事件委托给viewmodel。

+1 针对好的观点,即在视图模型上使用 ICommands 比在代码后台中添加一些小的“中间逻辑”更需要编写/复杂性。即便如此,这种方法仍然缺乏一个逻辑的 Execute/CanExecute 配对,这在视图模型中是很好拥有的。 - HappyNomad

1
由于这个问题标题的措辞方式,读者们可能是在寻找一个替代ICommand的方法,而不仅仅是一种直接将UI操作绑定到ViewModel方法的方式。使用ICommand本身就存在问题,因为它是在Windows.Input中定义的,这意味着为了在你的ViewModels中声明ICommands,你必须从你的应用逻辑中引用WPF和更多组件。一旦你这样做了,任何新手都可以注意到MessageBox和其它大量GUI功能可用,他们可能会开始使用它,导致应用逻辑和表现逻辑混乱。所以,如果你想摆脱using System.Windows,则需要摆脱ICommand,如果你想摆脱ICommand,则你可能会很高兴知道以下内容:

WPF(特别是XAML设计师)不要求您的viewModels静态公开实现ICommand接口的实例。

这里的静态指的是设计器在设计时不需要使用反射来证明您的命令对象实现了ICommand接口;相反,WPF会在运行时检查UI操作是否绑定到确实实现ICommand的对象上。

因此,在您的viewModels(应用程序逻辑)中,您可以使用自己设备的一些Command接口,而您需要确保在运行时实例化以实现您的Command接口的类也实现了ICommand以使WPF满意。这样,您就可以避免在ViewModels中包含ICommand,并且随后您可能能够避免在应用程序逻辑中引用System.Windows


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接