簡體   English   中英

在WPF MVVM ViewModels中簡化RelayCommand / DelegateCommand

[英]Simplifying RelayCommand/DelegateCommand in WPF MVVM ViewModels

如果您正在使用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#或垃圾收集器,知道這是否會導致問題,例如在某些情況下產生過多的垃圾。 這會造成任何問題嗎?

這與您提供計算某個常量值的 - - 整數 - 屬性完全相同。 您可以為get-method上的每個調用計算它,也可以在第一次調用時創建它,然后對其進行緩存,以便為以后的調用返回緩存的值。 因此,如果最多只調用一次getter,它確實沒有任何區別,如果經常調用它,你將失去一些(不多)性能,但你不會遇到真正的麻煩。

我個人喜歡縮寫MSDN這樣的方式:

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

我發現如果你有多個控件調用相同的命令,你需要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將避免額外的分配。

我做的一件事是讓Visual Studio為我打字。 我剛剛創建了一個代碼片段,允許我通過輸入創建一個RelayCommand

rc 選項卡保存回車

rc是代碼片段快捷方式選項卡,可以加載您鍵入的文本,並創建所有其他措辭。

一旦你看到一個代碼片段並創建自己的代碼片段,你將永遠不會回去:)

有關創建代碼段的詳細信息,請訪問: http//msdn.microsoft.com/en-us/library/ms165394.aspx

當您在viewmodel上公開ICommand屬性並且它沒有支持字段時,這是可以的,只要您只綁定一次該字段即可。

CommandWrapper的GetCommand方法將返回命令(如果已創建)。

你為什么不寫作:

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

public ICommand SaveCommand { get { return _saveCommand; } }

當您在viewmodel上公開ICommand屬性並且它沒有支持字段時,這是可以的,只要您只綁定一次該字段即可。 基本上,當表單加載並執行初始綁定時,這是它唯一一次訪問命令的get屬性。

很多時候你只會一次綁定一個命令。

如果將同一命令綁定到多個控件,則需要支持字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM