Как внедрить действие в команду с помощью Ninject?
На самом деле изучает шаблон команд и находит его довольно интересным. Я пишу WPF-приложение для Windows по архитектурному шаблону MVVM.
Я начал с этого поста, который объясняет основы.
- Базовый пример использования MVVM и ICommand
- Упрощение проектирования распределенной системы с помощью шаблона команд, MSMQ и.NET
Теперь, когда я смог разбить действия пользователя на команды, я подумал, что было бы здорово добавить нужные мне команды. Я заметил, что команды находятся в ViewModel в первой статье, на которую есть ссылки, поэтому я подумал, что было бы здорово, если бы я мог использовать их вдоль Ninject и фактически внедрить свою команду в мою модель представления, используя привязку, которая будет выглядеть следующим образом:
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("execute", new Action<object>(???));
Но тогда, что положить сюда??? Ожидаемый ответ - метод. Большой! Мне просто нужен метод, чтобы положить туда.
Поскольку первая статья просто инициализирует свои команды в конструкторе ViewModel, легко сказать, какой метод должен быть выполнен при вызове команды execute.
Но изнутри CompositionRoot? Здесь не место для метода, который будет делать что-то еще, кроме связывания типов, через какой-либо контейнер DI, который вы используете!
Итак, теперь я наткнулся на шаблон перехватчика, используя Ninject Extensions. Похоже, что это может удовлетворить мои требования, и здесь есть некоторая путаница, если я могу сказать. Не то чтобы статьи сбивают с толку, это не так. Я не совсем понимаю!
Использование Ninject.Extensions.Interception Часть 1: Основы
Использование Ninject.Extensions.Interception. Часть 2. Работа с перехватчиками
Также есть ответ от BatteryBackupUnit, который всегда задает отличные ответы.
Но сейчас я не вижу, как все это склеить! Смиренно, я потерян.
Итак, вот мой код.
RelayCommand
public class RelayCommand : ICommand {
public RelayCommand(Action<object> methodToExecute, Predicate<object> canExecute) {
if(methodToExecute == null)
throw new ArgumentNullException("methodToExecute");
if(canExecute == null)
throw new ArgumentNullException("canExecute");
this.canExecute = canExecute;
this.methodToExecute = methodToExecute;
}
public bool CanExecute(object parameter) {
return canExecute != null && canExecute(parameter);
}
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
canExecuteChanged += value;
}
remove {
CommandManager.RequerySuggested -= value;
canExecuteChanged -= value;
}
}
public static bool DefaultCanExecute(object parameter) { return true; }
public void Execute(object parameter) { methodToExecute(parameter); }
public void OnCanExecuteChanged() {
var handler = canExecuteChanged;
if(handler != null) handler(this, EventArgs.Empty);
}
public void Destroy() {
canExecute = _ => false;
methodToExecute = _ => { return; };
}
private Predicate<object> canExecute;
private Action<object> methodToExecute;
private event EventHandler canExecuteChanged;
}
CategoriesManagementViewModel
public class CategoriesManagementViewModel : ViewModel<IList<Category>> {
public CategoriesManagementViewModel(IList<Category> categories
, ICommand changeCommand
, ICommand createCommand
, ICommand deleteCommand) : base(categories) {
if(changeCommand == null)
throw new ArgumentNullException("changeCommand");
if(createCommand == null)
throw new ArgumentNullException("createCommand");
if(deleteCommand == null)
throw new ArgumentNullException("deleteCommand");
this.changeCommand = changeCommand;
this.createCommand = createCommand;
this.deleteCommand = deleteCommand;
}
public ICommand ChangeCommand { get { return changeCommand; } }
public ICommand CreateCommand { get { return createCommand; } }
public ICommand DeleteCommand { get { return deleteCommand; } }
private readonly ICommand changeCommand;
private readonly ICommand createCommand;
private readonly ICommand deleteCommand;
}
Интересно, будет ли лучше использовать Property Injection, хотя я склонен не использовать все это?
Допустим, у меня есть CategoriesManagementView
это вызывает другое окно, скажем CreateCategoryView.Show()
, а затем CreateCategoryView вступает во владение, пока пользователь не вернется в окно управления.
Команде Create затем нужно вызвать CreateCategoryView.Show(), и это то, что я пытался из CompositionRoot.
CompositionRoot
public class CompositionRoot {
public CompositionRoot(IKernel kernel) {
if(kernel == null) throw new ArgumentNullException("kernel");
this.kernel = kernel;
}
//
// Unrelated code suppressed for simplicity sake.
//
public IKernel ComposeObjectGraph() {
BindCommandsByConvention();
return kernel;
}
private void BindCommandsByConvention() {
//
// This is where I'm lost. I can't see any way to tell Ninject
// what I want it to inject into my RelayCommand class constructor.
//
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("methodToExecute", new Action<object>());
//
// I have also tried:
//
kernel
.Bind<ICommand>()
.ToConstructor(ctx =>
new RelayCommand(new Action<object>(
ctx.Context.Kernel
.Get<ICreateCategoryView>().ShowSelf()), true);
//
// And this would complain that there is no implicit conversion
// between void and Action and so forth.
//
}
private readonly IKernel kernel;
}
Возможно, я слишком усложняю вещи, это то, что обычно случается, когда кто-то запутывается. знак равно
Мне просто интересно, может ли Ninject Interception Extension быть правильным инструментом для работы и как его эффективно использовать?
1 ответ
Я создал простой пример команды, взаимодействующей с внедренным сервисом. может не скомпилироваться, так как я ухожу из памяти. Может быть, это может помочь вам.
public class TestViewModel
{
private readonly IAuthenticationService _authenticationService;
public DelegateCommand SignInCommand { get; private set; }
public TestViewModel(IAuthenticationService authenticationService) //Inject auth service
{
_authenticationService = authenticationService
SignInCommand = new DelegateCommand(OnSignInRequest)
}
private void OnSignInRequest(Action<bool> isSuccessCallback)
{
var isSuccess = _authenticationService.SignIn();
isSuccessCallback(isSuccess);
}
}
}