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

Реализация WPF 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
}
4b9b3361

Ответ 1

Это почти идентично тому, как показал Карл Шиффлет 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;
        }
    }
}

Ответ 2

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

Teaser:

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

}

UPDATE: теперь, похоже, существуют некоторые библиотеки, такие как http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model, которые решают проблему шаблона ICommand.

Ответ 3

Я написал эту статью об интерфейсе 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;
}

Ответ 4

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

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