如何使用Ninject将操作注入命令?

时间:2015-03-13 14:36:54

标签: wpf mvvm ninject command-pattern ninject-interception

实际上正在探索Command Pattern并发现它非常有趣。我正在MVVM Architectural Pattern之后编写一个WPF Windows应用程序。

我已经开始讲解这些基础知识。

既然我能够将用户操作分解为命令,我认为注入我想要的命令会很棒。我注意到在第一篇引用的文章中找到了 ViewModel 中的命令,所以我认为如果我可以在Ninject中使用它们并且实际上将我的命令注入我的视图模型中会很棒使用如下所示的绑定:

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

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

因为第一篇文章只是简单地在ViewModel构造函数中初始化它的命令,所以很容易说出命令执行调用应该执行什么方法。

但是来自CompositionRoot?除了你正在使用的DI容器之外,没有地方可以放置一个除了将类型绑定在一起之外的任何方法!

所以现在,我遇到了使用Ninject Extensions的拦截器模式。这似乎符合我的要求,如果我可以说,这里有一点混乱。这些文章并不令人困惑,但事实并非如此。我很困惑!

此外,还有来自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 Command然后需要调用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 个答案:

答案 0 :(得分: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);
    }
}


}