如果您正在使用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#或垃圾收集器,知道这是否会导致问题,例如在某些情况下产生过多的垃圾。这会造成任何问题吗?
答案 0 :(得分:17)
这与您提供计算某个常量值的 - - 整数 - 属性完全相同。 您可以为get-method上的每个调用计算它,也可以在第一次调用时创建它,然后对其进行缓存,以便为以后的调用返回缓存的值。 因此,如果最多只调用一次getter,它确实没有任何区别,如果经常调用它,你将失去一些(不多)性能,但你不会遇到真正的麻烦。
我个人喜欢缩写MSDN这样的方式:
RelayCommand _saveCommand;
public ICommand SaveCommand
{
get
{
return _saveCommand ?? (_saveCommand = new RelayCommand(param => this.Save(),
param => this.CanSave ));
}
}
答案 1 :(得分: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将避免额外的分配。
答案 2 :(得分:7)
我做的一件事是让Visual Studio为我打字。我刚刚创建了一个代码片段,允许我通过键入
来创建一个RelayCommandrc 选项卡保存输入
rc是代码段快捷方式 选项卡加载您键入的文本,然后创建所有其他措辞。
一旦你看了一个代码片段并创建了自己的代码片段,你将永远不会回去:)
有关创建代码段的详细信息:http://msdn.microsoft.com/en-us/library/ms165394.aspx
答案 3 :(得分:1)
你为什么不写:
private readonly RelayCommand _saveCommand = new RelayCommand(param => this.Save(),
param => this.CanSave );;
public ICommand SaveCommand { get { return _saveCommand; } }
答案 4 :(得分:1)
当你在viewmodel上公开ICommand属性并且它没有支持字段时,只要你只绑定一次该字段就可以了。
如果已经创建了命令,则CommandWrapper的GetCommand方法将返回该命令。
答案 5 :(得分:0)
当你在viewmodel上公开ICommand属性并且它没有支持字段时,只要你只绑定一次该字段就可以了。基本上,当表单加载并执行初始绑定时,这是它唯一一次访问命令的get属性。
很多时候你只会一次绑定一个命令。
如果将同一命令绑定到多个控件,则需要支持字段。