在WPF MVVM ViewModels中简化RelayCommand/DelegateCommand

Ben*_*pke 19 c# wpf mvvm relaycommand delegatecommand

如果您正在使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这很麻烦,并且使得设置新命令相当繁琐(我与一些资深的WinForms开发人员合作,他们对所有这些打字都不屑一顾).所以我想简化它并挖掘一下.我在get {}块的第一行设置了一个断点,看到它只是在我的应用程序第一次加载时才被击中 - 我可以随后发出尽可能多的命令,这个断点永远不会被击中 - 所以我想要简化这个以从我的ViewModel中删除一些混乱,并注意到以下代码的工作方式相同:

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

但是,我不太了解C#或垃圾收集器,知道这是否会导致问题,例如在某些情况下产生过多的垃圾.这会造成任何问题吗?

Sim*_* D. 17

这与您提供计算某个常量值的 - - 整数 - 属性完全相同.您可以为get-method上的每个调用计算它,也可以在第一次调用时创建它,然后对其进行缓存,以便为以后的调用返回缓存的值.因此,如果最多只调用一次getter,它确实没有任何区别,如果经常调用它,你将失去一些(不多)性能,但你不会遇到真正的麻烦.

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

RelayCommand _saveCommand;
public ICommand SaveCommand
{
  get
  {
    return _saveCommand ?? (_saveCommand = new RelayCommand(param => this.Save(),
                                                            param => this.CanSave ));
  }
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*pke 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您真的担心内存使用情况,那么仅实例化您要使用的命令是不是有意义?例如,如果您有一个暴露3个命令的VM,并且只使用了一个命令,那么只需删除其余命令.如果您正在使用所有VM命令(您应该使用),那么延迟加载系统将使用更多内存和更多处理器时间,这首先会破坏优化的目的,特别是对于嵌入式系统. (4认同)

Jos*_*ose 7

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

rc 选项卡保存回车

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

一旦你看到一个代码片段并创建自己的代码片段,你将永远不会回去:)

有关创建代码段的详细信息,请访问:http://msdn.microsoft.com/en-us/library/ms165394.aspx