Реализация MVVM ICommand WPF

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

то, что я сделал, - это один класс ICommand и два делегата, которые принимают объект в качестве параметра, один делегат-void (для 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 ответов


это почти идентично тому, как Карл Шиффлет показал a 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. Вызывается из элементов пользовательского интерфейса для переключения состояния .

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;
}

Я только что создал немного пример показывает, как реализовать команды в соответствии со стилем конфигурации. Однако это требует размышлений.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 и добавил null в конструктор

затем использовать в 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
        });
    }
}

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