【发布时间】:2015-03-13 14:36:53
【问题描述】:
实际探索Command Pattern,发现它非常有趣。我正在按照MVVM Architectural Pattern 编写一个WPF Windows 应用程序。
我从这些解释基础知识的帖子开始。
- Basic MVVM and ICommand usuage example
- Simplify Distributed System Design Using the Command Pattern, MSMQ, and .NET
现在我能够将用户操作分解为命令,我认为这可以很好地注入我想要的命令。我注意到在第一篇引用的文章中可以在 ViewModel 中找到这些命令,所以我认为如果我可以将它们与Ninject 一起使用并实际使用如下所示的绑定:
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("execute", new Action<object>(???));
但是,这里放什么???。预期的答案是一种方法。伟大的!我只需要一个方法放在那里。
因为第一篇文章只是简单的在 ViewModel 构造函数中初始化了它的命令,所以很容易说在命令执行调用上应该执行什么方法。
但是来自CompositionRoot?除了通过您使用的任何 DI 容器将类型绑定在一起之外,这里没有放置任何其他方法的方法!
所以现在,我遇到了使用 Ninject 扩展的拦截器模式。这看起来可以满足我的要求,如果我可以说的话,这里有点混乱。并不是说这些文章令人困惑,他们不是。我很困惑!
此外,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;
}
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 接管,直到用户返回到管理窗口。
然后,创建命令需要调用 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 是否适合这项工作,以及如何有效地使用它?
【问题讨论】:
-
我认为注入动作是错误的方法。 VM 中的命令应该在命令方法中使用注入服务。
-
这是否意味着,例如,我的
createCommand参数将是一个类,而该类又依赖于我的CreateCategoryView,然后 ICommand.Execute 将简单地调用依赖项的Show()方法? -
你只需要在你的虚拟机中声明命令并绑定它。例如: public DelegateCommand SignInCommand { get;私人套装; }。在您的实现中,引用您的注入服务(您的 API、AuthenticationService 等)
-
这是我的意图,我似乎没有找到解决方法。这正是我的 ViewModel 中所拥有的,一个类型为 ICommand 的属性,其访问修饰符设置为 public,然后可以通过数据绑定调用 ViewModel 的命令。这个,我明白了。我不知道如何正确构建我的命令。我的命令是否需要直接依赖于我的服务?我猜...
-
您一定要阅读this blog post。在您的情况下,命令模式可能不是最好的模式。
标签: wpf mvvm ninject command-pattern ninject-interception