RelayCommand 最佳实践

RelayCommand best practice

我正在使用 RelayCommands(它们在单独的 class 中)大约一个月了,我感觉它们在声明时有点笨拙。下面我有 3 种方法可以想到如何声明 RelayCommand.

在第一种情况下,我声明了我的 ICommand,然后在加载 ViewModel 时,我构造了我的 RelayCommand,它指向我代码中的一个方法。

public class MyViewModel
{
    public ICommand MyCommand { get; private set; }

    public MyViewModel()
    {
        MyCommand = new RelayCommand(MyMethod, CanMyMethod);
    }

    private void MyMethod()
    {
         // Do something here ...
    }

    private bool CanMyMethod()
    {
         return string.IsNullOrEmpty(MyString) ? false : true;
    }
}

第二种方法是一次完成所有事情。

public ICommand MyCommand
    {
        get
        {
            return new RelayCommand(
                () =>
                {
                    // Do something here ...
                },
                () =>
                    string.IsNullOrEmpty(MyString) ? false : true);
        }
    }

现在,我打算在某个 ViewModel 中编写一个包含相当多 Commands 的应用程序。我也无法将 ViewModel 拆分为更小的 ViewModel,因为所有 controls 都必须协同工作。

所以我的问题是:

  1. 声明和构造 ICommands 的最佳方法是什么?这是我的一种方法还是有更简单的方法?
  2. 考虑到单个 ViewModel 中有 50 多个 ICommands,维护每种方法的概览有多难。
  3. 我希望将来在 Windows 7、8 和 10 上发布我的应用程序。 RelayCommands 如果我只使用 .NET4.5,是否有任何限制我必须考虑?
  4. 除了RelayCommands我还找到了这个项目:Caliburn-Micro。它允许您执行类似以下代码的操作。有谁知道与 RelayCommands 相比,这在性能方面有多好?这只是一个额外的问题,不需要回答就可以将 post 标记为答案。

Xaml(查看)

<Button x:Name="Login" Content="Log in" />

视图模型

public bool CanLogin(string username, string password)
{
    return !String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password);
}

public string Login(string username, string password)
{
    ...
}

我同意 Krowi 的观点,第一种方法更容易阅读。但我会把你的中继命令放到它自己的 class 中,这样你就可以重新使用它:

public class RelayCommand : ICommand
{
    #region Fields
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;
    #endregion // Fields

    #region Constructors
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion

    #region ICommand Members
    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
    #endregion
}

参考以下答案。

  1. 声明和构造 ICommand 的最佳方法是什么? 这是我的方法之一还是有更简单的方法? 答:可以 采取结合的方式。如果您的执行方法非常少,您 可以使用 RelayCommnd 否则你可以实现你自己的 ICommand 一个单独的 class。这将提高您的视图模型的可读性 以及代码的模块化。
  2. 考虑到单个 ViewModel 中有 50 多个 ICommand,维护每种方法的概览有多难。 Ans:覆盖在 Ans 1
  3. 我希望将来在 Windows 7、8 和 10 上发布我的应用程序。如果我只使用 .NET4.5,我必须考虑 RelayCommands 有什么限制吗? 回答:我在 Windows 7 或 8 中没有看到任何限制,但不确定 Windows 10。
  4. 除了RelayCommands,我还找到了这个项目:Caliburn-Micro。它允许您执行类似以下代码的操作。有谁知道与 RelayCommands 相比,这在性能方面有多好?这只是一个额外的问题,不需要回答就可以将 post 标记为答案。 Ans:我不确定Caliburn-Micro 中RelayCommands 的源代码。但是如果它使用 CommandManager 来实现 CanExecute 功能。然后命令管理器将在所有用户输入更改时被触发,因此如果 CanExecute 方法中有一些繁重的逻辑将导致性能问题。 参考 Are there any performance implications with CanExecuteCommand?

这是我更喜欢的模式,它基本上是方法 1 的变体:

public class MyViewModel
{
    private readonly RelayCommand myCommand;

    public MyViewModel()
    {
        this.myCommand = new RelayCommand(this.DoStuff, () => ...);
    }

    public ICommand MyCommand
    {
        get { return this.myCommand; }
    }

    private void DoStuff() { ... }
}

这样做的好处是可以在您的 RelayCommand 实现上保留额外的方法(例如 RaiseCanExecuteChanged)供您的视图模型使用,但只向消费者公开一个 ICommand 实例。