如何注入动作进入使用Ninject的命令?

问题描述:

其实探索Command Pattern,并发现它非常有趣。我正在编写MVVM Architectural Pattern之后的WPF Windows应用程序。如何注入动作进入使用Ninject的命令?

我已经开始与这些帖子里面讲解的基础知识。

现在,我能打破用户操作为命令,我想这可能是巨大的,注入我想要的命令。我注意到,该命令被发现到视图模型第一引用的文章中,所以我想这将是巨大的,如果我能沿着Ninject使用它们,实际使用的绑定看起来像下面注入我的命令到我的视图模型:

kernel 
    .Bind<ICommand>() 
    .To<RelayCommand>() 
    .WithConstructorArgument("execute", new Action<object>(???)); 

但是,那么,在这里放什么???。预期的答案是一种方法。大!我只需要一种方法放在那里。

因为第一篇文章只是初始化视图模型构造函数中的命令,很容易地说应在命令执行调用执行什么方法。

但是从CompositionRoot内?这不是放置一种方法的地方,它会做任何事情,而不是通过你使用的任何DI容器将类型绑定在一起!

所以现在我已经遇到了使用Ninject扩展的拦截器模式。这看起来可以满足我的要求,如果我可以这样说,这里有点混乱。不是说文章混乱,他们不是。我很困惑!

此外,还有来自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接管直到用户回到管理窗口。

创建命令然后需要调用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; 
} 

也许我是过于复杂的东西,通常是当一个迷糊什么happends。 =)

我只是想知道Ninject截取扩展是否可以成为这项工作的正确工具,以及如何有效地使用它?

+0

我认为注射动作是错误的做法。虚拟机中的命令应该在命令方法内使用注入服务。 – vidalsasoon 2015-03-13 14:55:41

+0

这是否意味着,例如,我的'createCommand'参数是一个类,而该类又取决于我的'CreateCategoryView',然后ICommand.Execute会简单地调用依赖的'Show()'方法? – 2015-03-13 15:07:48

+0

你只需要在虚拟机中声明命令并绑定到它。例如:public DelegateCommand SignInCommand {get;私人设置; }。在你的实现中,引用你的注入服务(你的API,AuthenticationService等) – vidalsasoon 2015-03-13 15:42:12

我创建了一个与注入服务交互的命令的简单示例。可能因为我从记忆中而不能编译。也许这可以帮助你。

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); 
    } 
} 


}