Внедрение ICommand MVVM

Так что в этой конкретной реализации MVVM я делаю, мне нужно несколько команд. Я действительно устал от реализации классов ICommand один за другим, поэтому я придумал решение, но я не знаю, насколько оно хорошо, поэтому вклад любого эксперта WPF здесь будет высоко оценен. И если бы вы могли предоставить лучшее решение, даже лучше.

То, что я сделал, это один класс ICommand и два делегата, которые принимают объект в качестве параметра, один делегат является недействительным (для OnExecute), другой bool (для OnCanExecute). Поэтому в конструкторе моей ICommand (которая вызывается классом ViewModel) я отправляю два метода, и для каждого метода ICommand я вызываю методы делегатов.

Это работает действительно хорошо, но я не уверен, что это плохой способ сделать это или есть лучший способ. Ниже приведен полный код, любые входные данные будут высоко оценены, даже отрицательные, но, пожалуйста, будьте конструктивны.

ViewModel:

public class TestViewModel : DependencyObject
{
    public ICommand Command1 { get; set; }
    public ICommand Command2 { get; set; }
    public ICommand Command3 { get; set; }

    public TestViewModel()
    {
        this.Command1 = new TestCommand(ExecuteCommand1, CanExecuteCommand1);
        this.Command2 = new TestCommand(ExecuteCommand2, CanExecuteCommand2);
        this.Command3 = new TestCommand(ExecuteCommand3, CanExecuteCommand3);
    }

    public bool CanExecuteCommand1(object parameter)
    {
        return true;
    }

    public void ExecuteCommand1(object parameter)
    {
        MessageBox.Show("Executing command 1");
    }

    public bool CanExecuteCommand2(object parameter)
    {
        return true;
    }

    public void ExecuteCommand2(object parameter)
    {
        MessageBox.Show("Executing command 2");
    }

    public bool CanExecuteCommand3(object parameter)
    {
        return true;
    }

    public void ExecuteCommand3(object parameter)
    {
        MessageBox.Show("Executing command 3");
    }
}

ICommand:

public class TestCommand : ICommand
{
    public delegate void ICommandOnExecute(object parameter);
    public delegate bool ICommandOnCanExecute(object parameter);

    private ICommandOnExecute _execute;
    private ICommandOnCanExecute _canExecute;

    public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
    {
        _execute = onExecuteMethod;
        _canExecute = onCanExecuteMethod;
    }

    #region ICommand Members

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute.Invoke(parameter);
    }

    public void Execute(object parameter)
    {
        _execute.Invoke(parameter);
    }

    #endregion
}

4 ответа

Решение

Это почти идентично тому, как Карл Шиффлет продемонстрировал RelayCommand, где Execute стреляет заранее Action<T>, Первоклассное решение, если вы спросите меня.

public class RelayCommand : ICommand
{
    private Predicate<object> _canExecute;
    private Action<object> _execute;

    public RelayCommand(Predicate<object> canExecute, Action<object> execute)
    {
        this._canExecute = canExecute;
        this._execute = execute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

Это может быть использовано как...

public class MyViewModel
{
    private ICommand _doSomething;
    public ICommand DoSomethingCommand
    {
        get
        {
            if (_doSomething == null)
            {
                _doSomething = new RelayCommand(
                    p => this.CanDoSomething,
                    p => this.DoSomeImportantMethod());
            }
            return _doSomething;
        }
    }
}

Прочитайте больше:
Джош Смит (ведущий RelayCommand ): Шаблоны - Приложения WPF с шаблоном проектирования MVVM

Я написал эту статью об интерфейсе ICommand.

Идея - создать универсальную команду, которая принимает двух делегатов: одна вызывается, когда ICommand.Execute (object param) вызывается, вторая проверяет статус, можете ли вы выполнить команду (ICommand.CanExecute (object param)),

Требуется метод для переключения событий CanExecuteChanged, Вызывается из элементов пользовательского интерфейса для переключения состояния CanExecute() команда.

public class ModelCommand : ICommand
{
    #region Constructors

    public ModelCommand(Action<object> execute)
        : this(execute, null) { }

    public ModelCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return _canExecute != null ? _canExecute(parameter) : true;
    }

    public void Execute(object parameter)
    {
        if (_execute != null)
            _execute(parameter);
    }

    public void OnCanExecuteChanged()
    {
        CanExecuteChanged(this, EventArgs.Empty);
    }

    #endregion

    private readonly Action<object> _execute = null;
    private readonly Predicate<object> _canExecute = null;
}

Я только что создал небольшой пример, показывающий, как реализовать команды в соответствии со стилем конфигурации. Однако это требует, чтобы Reflection.Emit() был доступен. Вспомогательный код может показаться немного странным, но после написания его можно использовать много раз.

Тизер:

public class SampleViewModel: BaseViewModelStub
{
    public string Name { get; set; }

    [UiCommand]
    public void HelloWorld()
    {
        MessageBox.Show("Hello World!");
    }

    [UiCommand]
    public void Print()
    {
        MessageBox.Show(String.Concat("Hello, ", Name, "!"), "SampleViewModel");
    }

    public bool CanPrint()
    {
        return !String.IsNullOrEmpty(Name);
    }
}

}

ОБНОВЛЕНИЕ: теперь, кажется, существуют некоторые библиотеки, такие как http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model которые решают проблему стандартного кода ICommand.

@Carlo Мне очень нравится ваша реализация этого, но я хотел поделиться своей версией и как использовать ее в моей ViewModel

Сначала внедрите ICommand

public class Command : ICommand
{
    public delegate void ICommandOnExecute();
    public delegate bool ICommandOnCanExecute();

    private ICommandOnExecute _execute;
    private ICommandOnCanExecute _canExecute;

    public Command(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod = null)
    {
        _execute = onExecuteMethod;
        _canExecute = onCanExecuteMethod;
    }

    #region ICommand Members

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute?.Invoke() ?? true;
    }

    public void Execute(object parameter)
    {
        _execute?.Invoke();
    }

    #endregion
}

Обратите внимание, что я удалил параметр из ICommandOnExecute и ICommandOnCanExecute и добавил нуль в конструктор

Затем использовать в ViewModel

public Command CommandToRun_WithCheck
{
    get
    {
        return new Command(() =>
        {
            // Code to run
        }, () =>
        {
            // Code to check to see if we can run 
            // Return true or false
        });
    }
}

public Command CommandToRun_NoCheck
{
    get
    {
        return new Command(() =>
        {
            // Code to run
        });
    }
}

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

Другие вопросы по тегам