Внедрение 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
});
}
}
Я просто нахожу этот способ чище, поскольку мне не нужно присваивать переменные, а затем создавать экземпляры, все это делается за один раз.