Подтвердить что ты не робот

Упрощение RelayCommand/DelegateCommand в WPF MVVM ViewModels

Если вы используете MVVM и используете команды, вы часто увидите свойства ICommand в ViewModel, которые поддерживаются частными полями RelayCommand или DelegateCommand, как этот пример из оригинальной статьи MVVM на MSDN:

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

Однако, это много беспорядка, и делает настройку новых команд довольно утомительной (я работаю с некоторыми ветеранскими разработчиками WinForms, которые вообще не выполняют эту типизацию). Поэтому я хотел упростить его и немного выкопать. Я установил точку останова в первой строке блока {} и увидел, что она только попала, когда мое приложение было впервые загружено - я могу позже сбить столько команд, сколько захочу, и эта точка останова никогда не попадает - так что я хотел упростить это, чтобы удалить некоторый беспорядок из моих ViewModels и заметил, что следующий код работает одинаково:

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

Однако я не знаю достаточно о С# или сборщике мусора, чтобы узнать, могут ли это вызвать проблемы, например, в некоторых случаях создавать избыточный мусор. Будет ли это представлять какие-либо проблемы?

4b9b3361

Ответ 1

Я обнаружил, что вам нужен оригинальный способ из 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, которые не имеют каких-либо команд, будут избегать дополнительных распределений.

Ответ 2

Это точно так же, как если бы вы предложили a - say integer - свойство, которое вычисляет некоторое постоянное значение. Вы можете либо вычислить его для каждого вызова метода get, либо создать его при первом вызове, а затем кэшировать его, чтобы вернуть кешированное значение для последующих вызовов. Поэтому, если геттер вызывается не чаще одного раза, это не имеет никакого значения вообще, если его часто вызывают, вы потеряете некоторую (не очень) производительность, но вы не получите реальных проблем.

Мне лично нравится сокращать MSDN-способ следующим образом:

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

Ответ 3

Одна вещь, которую я делаю, это позволить Visual Studio сделать для меня ввод. Я просто создал фрагмент кода, который позволяет мне создать RelayCommand, набрав

rc Вкладка Сохранить Enter

rc - ярлык фрагмента кода вкладка загружает текст, который вы вводите, что вы хотите, и создает все остальные формулировки.

Как только вы посмотрите на фрагмент кода и создадите свой собственный, вы никогда не вернетесь:)

Дополнительные сведения о создании фрагментов кода: http://msdn.microsoft.com/en-us/library/ms165394.aspx

Ответ 4

Почему бы вам просто не писать:

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

public ICommand SaveCommand { get { return _saveCommand; } }

Ответ 5

Когда вы открываете свойство ICommand на своей модели просмотра и у него нет поля поддержки, это нормально, пока вы только привязываетесь к этому полю один раз.

Метод GetCommand CommandWrapper вернет команду, если она уже создана.

Ответ 6

Когда вы показываете свойство ICommand на своей модели просмотра и у него нет поля поддержки, это нормально, пока вы только привязываетесь к этому полю один раз. В основном, когда ваша форма загружается и она выполняет начальные привязки, это единственный раз, когда вы получите доступ к свойству получения команды.

Существует много раз, когда вы будете связывать команду только один раз.

Если вы привязываете ту же команду к нескольким элементам управления, вам понадобится поле поддержки.