【问题标题】:How to inject an action into a command using Ninject?如何使用 Ninject 将动作注入到命令中?
【发布时间】:2015-03-13 14:36:53
【问题描述】:

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

我从这些解释基础知识的帖子开始。

现在我能够将用户操作分解为命令,我认为这可以很好地注入我想要的命令。我注意到在第一篇引用的文章中可以在 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


【解决方案1】:

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

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


}

【讨论】:

    猜你喜欢
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-21
    • 2014-05-13
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多