实际探索命令模式并发现它非常有趣。我正在按照MVVM Architectural Pattern编写 WPF Windows 应用程序。
我从这些解释基础知识的帖子开始。
现在我能够将用户操作分解为命令,我认为这可以很好地注入我想要的命令。我注意到在第一篇引用的文章中可以在ViewModel中找到这些命令,所以我认为如果我可以在Ninject中使用它们并使用如下所示的绑定将我的命令实际注入到我的视图模型中,那就太好了:
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("execute", new Action<object>(???));
但是,在这里放什么???预期的答案是一种方法。伟大的!我只需要一个方法放在那里。
因为第一篇文章只是简单地在 ViewModel 构造函数中初始化了它的命令,所以很容易说在命令执行调用上应该执行什么方法。
但是从CompositionRoot内部?除了通过您正在使用的任何 DI 容器将类型绑定在一起之外,这里没有放置任何其他方法的地方!
所以现在,我遇到了使用 Ninject Extensions 的拦截器模式。这看起来可以满足我的要求,如果我可以说的话,这里有点混乱。并不是说这些文章令人困惑,他们不是。我很困惑!
此外,BatteryBackupUnit 也给出了这个答案,他总是给出很好的答案。
但是现在,我不知道如何将它们粘在一起!谦虚,我迷路了。
所以这是我到目前为止的代码。
中继命令
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;
}
分类管理视图模型
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 接管,直到用户回到管理窗口。
然后,创建命令需要调用 CreateCategoryView.Show(),这就是我在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 拦截扩展是否可以成为这项工作的正确工具,以及如何有效地使用它?