实际上探索命令模式并发现它非常有趣。我正在按照MVVM架构模式编写一个WPF Windows应用程序。
我从这些解释基础知识的帖子开始。
- 基本 MVVM 和 ICommand 使用示例
- 使用命令模式、MSMQ 和 .NET 简化分布式系统设计
现在我能够将用户操作分解为命令,我认为注入我想要的命令可能很棒。我注意到在第一篇引用的文章中,这些命令可以在 ViewModel 中找到,所以我认为如果我可以在 Ninject 中使用它们并使用如下所示的绑定将我的命令实际注入我的视图模型,那就太好了:
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("execute", new Action<object>(???));
但是,在这里放什么???.预期的答案是一种方法。伟大!我只需要一种方法放在那里。
由于第一篇文章只是在 ViewModel 构造函数中初始化其命令,因此很容易说出应该在命令执行调用上执行什么方法。
但是从组合根内部?这里不是放置一个方法的地方,该方法除了通过您正在使用的任何 DI 容器将类型绑定在一起之外,还会执行任何其他操作!
所以现在,我遇到了使用 Ninject 扩展的拦截器模式。这看起来可以满足我的要求,如果我可以说,这里有点混乱。不是说这些文章令人困惑,它们不是。我很困惑!
Using Ninject.Extensions.Interception Part 1 : The Basics
Using Ninject.Extensions.Interception Part 2 : Using Interceptors
此外,还有来自BatteryBackupUnit的答案,他总是能给出很好的答案。
- Ninject - 如何使用 Ninject 实现命令模式?
但是现在,我看不出如何将它们粘在一起!谦卑地,我迷路了。
所以这是我到目前为止的代码。
中继命令
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;
}
类别管理视图模型
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;
}
我想知道,使用属性注入会更好吗,尽管我倾向于不全部使用它?
假设我有调用另一个窗口的CategoriesManagementView
,假设CreateCategoryView.Show()
,然后 CreateCategoryView 接管,直到用户返回到管理窗口。
然后,创建命令需要调用 CreateCategoryView.Show(),这就是我在 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 拦截扩展是否适合这项工作,以及如何有效地使用它?
我创建了一个与注入的服务交互的命令的简单示例。 可能无法编译,因为我是从内存中取出的。也许这可以帮助你。
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);
}
}
}