Поэтому в этой конкретной реализации MVVM, которую я делаю, мне нужно несколько команд. Я действительно устал от внедрения classов ICommand один за другим, поэтому я придумал решение, но я не знаю, насколько это хорошо, поэтому вклад любого эксперта WPF здесь будет очень благодарен. И если бы вы могли обеспечить лучшее решение, еще лучше!
То, что я сделал, это один class ICommand и два делегата, которые принимают объект как параметр, один делегат недействителен (для OnExecute), другой bool (для OnCanExecute). Поэтому в конструкторе моей ICommand (который вызывается classом ViewModel) я отправляю два метода, и по каждому методу ICommand я вызываю методы делегатов.
Он работает очень хорошо, но я не уверен, что это плохой способ сделать это, или если есть лучший способ. Ниже приведен полный код, любой ввод будет оценен по достоинству, даже отрицательный, но, пожалуйста, будьте конструктивными.
Это почти идентично тому, как Карл Шиффлет продемонстрировал RelayCommand , где Execute запускает предопределенное Action . Самое первоclassное решение, если вы спросите меня.
public class RelayCommand : ICommand { private Predicate
Затем это можно использовать как …
public class MyViewModel { private ICommand _doSomething; public ICommand DoSomethingCommand { get { if (_doSomething == null) { _doSomething = new RelayCommand( p => this.CanDoSomething, p => this.DoSomeImportantMethod()); } return _doSomething; } } }
Идея – создание универсальной команды, которая принимает два делегата: один вызывается при вызове ICommand.Execute (object param) , второй проверяет состояние выполнения команды (ICommand.CanExecute (object param)) .
Требуется метод переключения события CanExecuteChanged . Он вызывается из элементов пользовательского интерфейса для переключения команды CanExecute() .
public class ModelCommand : ICommand { #region Constructors public ModelCommand(Action execute) : this(execute, null) { } public ModelCommand(Action execute, Predicate 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 _execute = null; private readonly Predicate _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); } }
@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 }); } }
Я просто нахожу этот способ более чистым, так как мне не нужно назначать переменные, а затем создавать экземпляр, все это делается за один раз.