2015-03-13 77 views
0

其實探索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

回答

0

我創建了一個與注入服務交互的命令的簡單示例。可能因爲我從記憶中而不能編譯。也許這可以幫助你。

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


}