19

如果您正在执行 MVVM 并使用命令,您会经常在 ViewModel 上看到由私有 RelayCommand 或 DelegateCommand 字段支持的 ICommand 属性,例如MSDN上原始 MVVM 文章中的示例:

RelayCommand _saveCommand;
public ICommand SaveCommand
{
    get
    {
        if (_saveCommand == null)
        {
            _saveCommand = new RelayCommand(param => this.Save(),
                param => this.CanSave );
        }
        return _saveCommand;
    }
}

然而,这很混乱,并且使设置新命令相当乏味(我与一些对所有这些输入都犹豫不决的资深 WinForms 开发人员一起工作)。所以我想简化它并挖掘一点。我在 get{} 块的第一行设置了一个断点,发现它只在我的应用程序第一次加载时才被命中——我以后可以根据需要触发任意数量的命令,而这个断点永远不会被命中——所以我想要简化它以消除我的 ViewModel 中的一些混乱,并注意到以下代码的工作方式相同:

public ICommand SaveCommand
{
    get
    {
        return new RelayCommand(param => this.Save(), param => this.CanSave );
    }
}

但是,我对 C# 或垃圾收集器知之甚少,不知道这是否会导致问题,例如在某些情况下会产生过多的垃圾。这会带来什么问题吗?

4

6 回答 6

18

这与您提供一个计算某个常数值的——比如整数——属性完全相同。您可以为 get 方法上的每次调用计算它,也可以在第一次调用时创建它然后缓存它,以便为以后的调用返回缓存的值。因此,如果 getter 最多被调用一次,它确实没有任何区别,如果它被频繁调用,你会损失一些(不多)性能,但你不会遇到真正的麻烦。

我个人喜欢这样缩写 MSDN 方式:

RelayCommand _saveCommand;
public ICommand SaveCommand
{
  get
  {
    return _saveCommand ?? (_saveCommand = new RelayCommand(param => this.Save(),
                                                            param => this.CanSave ));
  }
}
于 2010-06-25T09:31:54.553 回答
8

我发现如果您有多个调用相同命令的控件,则需要 MSDN 的原始方式,否则每个控件都会新建自己的 RelayCommand。我没有意识到这一点,因为我的应用程序每个命令只有一个控件。

因此,为了简化 ViewModels 中的代码,我将创建一个命令包装类来存储(并延迟实例化)所有 RelayCommands 并将其放入我的 ViewModelBase 类中。这样用户就不必直接实例化 RelayCommand 或 DelegateCommand 对象,也不需要了解它们的任何信息:

    /// <summary>
    /// Wrapper for command objects, created for convenience to simplify ViewModel code
    /// </summary>
    /// <author>Ben Schoepke</author>
    public class CommandWrapper
    {
    private readonly List<DelegateCommand<object>> _commands; // cache all commands as needed

    /// <summary>
    /// </summary>
    public CommandWrapper()
    {
        _commands = new List<DelegateCommand<object>>();
    }

    /// <summary>
    /// Returns the ICommand object that contains the given delegates
    /// </summary>
    /// <param name="executeMethod">Defines the method to be called when the command is invoked</param>
    /// <param name="canExecuteMethod">Defines the method that determines whether the command can execute in its current state.
    /// Pass null if the command should always be executed.</param>
    /// <returns>The ICommand object that contains the given delegates</returns>
    /// <author>Ben Schoepke</author>
    public ICommand GetCommand(Action<object> executeMethod, Predicate<object> canExecuteMethod)
    {
        // Search for command in list of commands
        var command = (_commands.Where(
                            cachedCommand => cachedCommand.ExecuteMethod.Equals(executeMethod) &&
                                             cachedCommand.CanExecuteMethod.Equals(canExecuteMethod)))
                                             .FirstOrDefault();

        // If command is found, return it
        if (command != null)
        {
            return command;
        }

        // If command is not found, add it to the list
        command = new DelegateCommand<object>(executeMethod, canExecuteMethod);
        _commands.Add(command);
        return command;
    }
}

这个类也被 ViewModelBase 类延迟实例化,因此没有任何命令的 ViewModel 将避免额外的分配。

于 2010-06-24T17:24:38.033 回答
7

我做的一件事是让 Visual Studio 为我做打字。我刚刚创建了一个代码片段,允许我通过键入来创建 RelayCommand

rc Tab保存Enter

rc 是代码片段快捷方式选项卡,用于加载您键入的文本并创建所有其他措辞。

一旦您查看了一个代码片段并创建了自己的代码片段,您将永远不会回头 :)

有关创建代码片段的更多信息:http: //msdn.microsoft.com/en-us/library/ms165394.aspx

于 2010-06-25T03:39:13.900 回答
1

你为什么不写:

private readonly RelayCommand _saveCommand = new RelayCommand(param => this.Save(),
                param => this.CanSave );;

public ICommand SaveCommand { get { return _saveCommand; } }
于 2010-06-26T11:14:40.427 回答
1

当您在视图模型上公开 ICommand 属性并且它没有支持字段时,这是可以的,只要您只绑定到该字段一次。

如果命令已经创建,CommandWrapper 的 GetCommand 方法将返回该命令。

于 2012-04-04T12:19:41.263 回答
0

当您在视图模型上公开 ICommand 属性并且它没有支持字段时,这是可以的,只要您只绑定到该字段一次。基本上,当您的表单加载并执行初始绑定时,这是唯一一次访问您的命令的 get 属性。

很多时候你只会绑定一个命令一次。

如果您将同一命令绑定到多个控件,那么您将需要支持字段。

于 2011-03-29T16:34:08.547 回答