2010-06-24 116 views
18

如果您在使用MVVM并使用命令,您会经常在ViewModel上看到由私有RelayCommand或DelegateCommand字段支持的ICommand属性,就像本示例中的原始MVVM文章MSDN简化WPF MVVM中的RelayCommand/DelegateCommand ViewModels

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

然而,这是一个很大的混乱,并使得建立新的命令相当繁琐(我有一些资深的WinForms谁在这一切的打字放水开发人员的工作)。所以我想简化它并挖一点。我在get {}块的第一行设置了一个断点,并且发现只有当我的应用程序第一次加载时它才会被触发 - 我可以稍后根据需要触发尽可能多的命令,并且此断点不会被触发 - 所以我要简化这个从我的ViewModels去除一些杂波,发现下面的代码的工作原理相同:

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

不过,我不知道有足够的了解C#或垃圾收集知道这可能会导致问题,如在某些情况下产生过多的垃圾。这会造成任何问题吗?

回答

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类实例化,这样的ViewModels没有任何命令将避免额外的分配。

+4

如果你真的担心你的内存使用情况,那么仅仅实例化将要使用的命令是否合理?例如,如果您有一台虚拟机显示3条命令,并且只有一条命令被使用,则放弃其余部分。如果您正在使用所有虚拟机命令(您应该是),那么您的延迟加载系统将使用更多的内存和更多的处理器时间,这首先破坏了优化的目的,尤其是对于嵌入式系统。 – 2011-03-29 16:55:19

7

我做的一件事就是让Visual Studio为我打字。我刚刚创建的代码片段,让我通过键入

RC 标签创建RelayCommand保存输入

RC是代码片段快捷 标签加载文本键入你想要和它创造所有其他的措辞。

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

欲了解更多有关创建代码片段:http://msdn.microsoft.com/en-us/library/ms165394.aspx

+1

我不知道为什么没有人提到过这个。这就是我所做的,这是处理命令的样板代码的最好方法。所有这些懒惰 - >懒惰的东西实际上并没有在长期运行中保存任何时间或代码。 – 2011-03-29 16:50:05

+0

小心分享您的摘要? – 2012-09-20 09:07:04

+1

这里是我的http://pastebin.com/YHD8AUjR – 2012-09-20 09:39:53

17

这是完全一样的,如果你会提供一个 - 说整数 - 属性,计算一些常数值。 您可以为get-method上的每次调用计算它,也可以在第一次调用时创建它,然后对其进行缓存,以便为以后的调用返回缓存的值。因此,如果最多只调用一次吸气剂,它根本没有任何区别,如果它经常被调用,则会失去一些(不是很多)性能,但是不会遇到麻烦。

我个人比较喜欢的缩写MSDN路是这样的:

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

良好的通话。我喜欢你的空coalesce操作符的用法。我总是忘记那个。 – 2011-03-29 17:24:07

1

你为什么不写只是:

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

public ICommand SaveCommand { get { return _saveCommand; } } 
+0

我想是为了防止创建一个没有必要的对象,但我并不真正知道它使用的内存或创建命令的其他副作用... – jpsstavares 2010-06-28 09:06:37

+0

我们希望懒惰的实例化来节省内存。我最终添加了一些代码给ViewModelBase类,该类存储了一个List <>的RelayCommands,并且有一个名为GetCommand()的方法。因此,当我们实现ViewModel时,我们需要做的就是创建一个ICommand属性,该属性使用execute和canExecute委托调用GetCommand(),ViewModel实现者不需要了解有关RelayCommand/Delegate命令的任何信息。 – 2010-06-28 14:30:04

+5

我不会开始优化命令,因为命令的内存占用太小。 – jbe 2010-06-28 18:04:58

0

当你暴露在你的视图模型的ICommand的属性和它doesn没有支持领域,这是好的,只要你只绑定到这个领域一次。基本上,当你的表单加载并执行初始绑定时,这是唯一一次访问你的命令的get属性。

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

如果将相同的命令绑定到多个控件,则需要后台字段。

1

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

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