为啥选择 RelayCommand
Posted
技术标签:
【中文标题】为啥选择 RelayCommand【英文标题】:Why RelayCommand为什么选择 RelayCommand 【发布时间】:2014-04-12 17:05:24 【问题描述】:我最近在 WPF 中进行了很多编程,但我的 View 和 ViewModel 在这一点上并没有分开。嗯,它是部分的。我所有与文本框中的文本、标签的内容、数据网格中的列表相关的绑定......都是由带有 NotifyPropertyChanged 事件的常规属性完成的。
我处理按钮点击或文本更改的所有事件都是通过链接事件来完成的。现在,我想开始使用命令并找到这篇文章:http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute。它解释了如何设置 MVVM,但我对 RelayCommand
感到困惑。
它的作用是什么? 它可用于我表单中的所有命令吗? 当(a)某些文本框未填写时,如何使按钮禁用?
编辑 1:
对“我的表单中的所有命令都可用吗?”的一个很好的解释在这里回答:https://***.com/a/22286816/3357699
这是我目前的代码:https://***.com/a/22289358/3357699
【问题讨论】:
Is it useable for all commands in my form?
- 你在这里指的是什么命令?
Click
和 TextChanged
对于Click
,您可以直接将按钮的Command
DP 绑定到ViewModel 中的一些ICommand
。但是要绑定TextChanged
,您需要使用交互触发器在您的ViewModel 中绑定ICommand
。
RelayCommand 实现 ICommand 并让您定义在调用 Execute 时应使用的操作。这就是您最终为所有命令使用一个类的方式,您只需更改操作,而不是为实现 ICommand 的每个命令创建每个类。在 wpf 中指挥工作需要 ICommand。
我自己尝试了一些东西并发布了我得到的东西。我得到了它的工作,但还有 2 个问题我希望在这里得到回答,而不是提出一个新问题。
【参考方案1】:
命令用于将调用命令的语义和对象与执行命令的逻辑分开,即它将 UI 组件与需要在命令调用时执行的逻辑分开。因此,您可以使用测试用例单独测试业务逻辑,并且您的 UI 代码与业务逻辑松散耦合。
现在,话虽如此,让我们一一挑选您的问题:
它的作用是什么?
我已经添加了上面的详细信息。希望它能清除命令的使用。
它是否适用于我的表单中的所有命令?
一些控件暴露了Command DependencyProperty,如Button、MenuItem 等,其中注册了一些默认事件。对于 Button,它是 Click
事件。因此,如果您将 ViewModel 中声明的 ICommand
与 Button 的 Command DP 绑定,则每当单击按钮时都会调用它。
对于其他事件,您可以使用Interactivity triggers
进行绑定。请参考示例here 如何使用它们绑定到 ViewModel 中的ICommand
。
当(a)某些文本框不可用时,如何使按钮禁用 填写了吗?
您发布的链接没有提供RelayCommand
的完整实现。它缺少用于设置 CanExecute
谓词的重载构造函数,该谓词在启用/禁用您的命令绑定到的 UI 控件方面起着关键作用。
绑定TextBox
es 与ViewModel
和CanExecute
中的一些属性,如果任何绑定属性为null
或为空,则会自动禁用绑定命令的控件。
RelayCommand
的完整实现:
public class RelayCommand<T> : ICommand
#region Fields
readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of <see cref="DelegateCommandT"/>.
/// </summary>
/// <param name="execute">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
/// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
public RelayCommand(Action<T> execute)
: this(execute, null)
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
#endregion
#region ICommand Members
///<summary>
///Defines the method that determines whether the command can execute in its current state.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
///<returns>
///true if this command can be executed; otherwise, false.
///</returns>
public bool CanExecute(object parameter)
return _canExecute == null || _canExecute((T)parameter);
///<summary>
///Occurs when changes occur that affect whether or not the command should execute.
///</summary>
public event EventHandler CanExecuteChanged
add CommandManager.RequerySuggested += value;
remove CommandManager.RequerySuggested -= value;
///<summary>
///Defines the method to be called when the command is invoked.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
public void Execute(object parameter)
_execute((T)parameter);
#endregion
【讨论】:
这是一个很好的解释和例子,它是如何完成的,但恐怕还是有点困惑。我曾经在 C# 中编程,但在上学时不得不切换到 VB.NET,所以这不是问题,但是,我现在如何链接命令并在我的视图模型中说明它必须做什么,比如显示“消息框”与“文本框”的内容 您提供的链接中的用法非常清楚。还是让我简单解释一下 - 在 ViewModel 中创建一个 RelayCommand 实例,在执行方法中您可以编写MessageBox.Show(parameter)
,您将使用 CommandParameter
从 GUI 传递参数。在网上搜索,您会找到数百万个样本。
我自己尝试了一些东西并发布了我得到的东西。我得到了它的工作,但还有 2 个我希望在这里回答的问题,而不是提出一个新问题。
我刚刚在 youtube 上观看了这个视频,youtube.com/watch?v=ysWK4e2Mtco,19:30
他建议不要使用 CommandManager.RequerySuggested
小问题:微软建议避免使用Predicate<T>
,而使用Func<T, bool>
。【参考方案2】:
使用中继命令的好处是您可以将命令直接绑定到 ViewModel。通过以这种方式使用命令,您可以避免在代码隐藏视图中编写代码。
使用中继命令时,您必须提供两种方法。第一个提供命令是否可以执行的值(例如“CanExecuteSave”),而另一个将负责执行命令(“ExecuteSave”)。
【讨论】:
能否为我提供一个带有按钮和文本框的简单示例?按钮只能在文本框有文本时激活。在我在一开始发布的链接中,如何做到这一点有点令人困惑。编辑:到目前为止,您的答案已经解决了 1 个问题:) 想象一些带有属性“Text”的 ViewModel,它绑定到按钮的 text-property。由于中继命令逻辑完全在 ViewModel 中,您可以直接访问按钮的文本并验证其值。 我自己尝试了一些东西并发布了我得到的东西。我得到了它的工作,但还有 2 个问题我希望在这里得到回答,而不是提出一个新问题。 将命令的代码隔离在 Command 类本身中,而不是将其耦合到 ViewModel 不是更好吗?以上是关于为啥选择 RelayCommand的主要内容,如果未能解决你的问题,请参考以下文章
为啥选择 JMS 作为异步解决方案?为啥它比简单的实体 bean 更好?
为啥我得到“[__NSArrayI allKeys]:无法识别的选择器发送到实例”/为啥 NSDictionary 正在转换?
为啥我得到“[__NSArrayI allKeys]:无法识别的选择器发送到实例”/为啥 NSDictionary 正在转换?