为啥选择 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? - 你在这里指的是什么命令? ClickTextChanged 对于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 控件方面起着关键作用。

绑定TextBoxes 与ViewModelCanExecute 中的一些属性,如果任何绑定属性为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&lt;T&gt;,而使用Func&lt;T, bool&gt;【参考方案2】:

使用中继命令的好处是您可以将命令直接绑定到 ViewModel。通过以这种方式使用命令,您可以避免在代码隐藏视图中编写代码。

使用中继命令时,您必须提供两种方法。第一个提供命令是否可以执行的值(例如“CanExecuteSave”),而另一个将负责执行命令(“ExecuteSave”)。

【讨论】:

能否为我提供一个带有按钮和文本框的简单示例?按钮只能在文本框有文本时激活。在我在一开始发布的链接中,如何做到这一点有点令人困惑。编辑:到目前为止,您的答案已经解决了 1 个问题:) 想象一些带有属性“Text”的 ViewModel,它绑定到按钮的 text-property。由于中继命令逻辑完全在 ViewModel 中,您可以直接访问按钮的文本并验证其值。 我自己尝试了一些东西并发布了我得到的东西。我得到了它的工作,但还有 2 个问题我希望在这里得到回答,而不是提出一个新问题。 将命令的代码隔离在 Command 类本身中,而不是将其耦合到 ViewModel 不是更好吗?

以上是关于为啥选择 RelayCommand的主要内容,如果未能解决你的问题,请参考以下文章

为啥选择 JMS 作为异步解决方案?为啥它比简单的实体 bean 更好?

为啥我得到“[__NSArrayI allKeys]:无法识别的选择器发送到实例”/为啥 NSDictionary 正在转换?

为啥我得到“[__NSArrayI allKeys]:无法识别的选择器发送到实例”/为啥 NSDictionary 正在转换?

为啥我有两个选择器?

为啥jQuery没有选择这个按钮

为啥 UIImagePickerControllerSourceTypePhotoLibrary 不让用户选择相册?