与其他问题相关:How to inject an action into a command using Ninject?
根据对上述问题的评论,我认为我只需要创建一些命令类并将它们注入(inject)到我的 View 模型中,以便 View 的控件只需要绑定(bind)到它们。我在概念上同意并理解这些好处。此外,我希望使用 Ninject、DI 和构造函数注入(inject)尽可能干净。
遵循这些重要的规则,这就是我到目前为止所带来的。
创建类别命令
public class CreateCategoryCommand : ICommand {
public CreateCategoryCommand(CreateCategoryView view) {
if(view == null) throw new ArgumentNullException("view");
this.view = view;
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) { view.Show(); }
private readonly CreateCategoryView view;
}
分类管理ViewModel
public class CategoriesManagementViewModel {
public CategoriesManagementViewModel(ICommand createCommand) {
if (createCommand == null) throw new ArgumentNullException("createCommand");
this.createCommand = createCommand;
}
public ICommand CreateCommand { get { return createCommand; } }
private readonly ICommand createCommand;
}
所以现在当分类管理查看 被初始化,它被构造函数注入(inject) 分类管理ViewModel ,而它又被构造函数注入(inject) 创建类别命令 ,而它又被构造函数注入(inject) 创建类别查看 ,所以没有多余的依赖,也没有任何循环依赖。
现在,当我 CategoriesManagementView.CreateButton ,它将触发绑定(bind) CategoriesManagementViewModel.CreateCommand ,这将显示 创建类别查看 给用户,并且这个 View 应该有它自己的正确命令以及以同样的方式注入(inject)。
最后,这将呈现 中继命令类无用...
是这样吗?
最佳答案
首先,我同意 RelayCommand
和 DelegateCommand
等是执行违反 SOLID 原则的命令的方法,因此您在这里用单独的类替换它们的解决方案是正确的。这样做还可以使您的 ViewModel 更加干净。
也就是说,您在 ViewModels 层中有一个类( CreateCategoryCommand
)知道您的 Views 层( CreateCategoryView
)中的具体知识,从而严重违反了 MVVM。 ViewModels 层中的任何内容都不应直接引用 Views 层中的任何内容。
以这种方式想象一下——你已经将你的层分成不同的 dll——Views.dll、ViewModels.dll、Models.dll、DataLayer.dll。如果您的 ViewModels 中的某些内容引用了您的 Views 中的具体内容,并且显然您的 Views 将引用 ViewModels(根据需要),那么您就有循环引用问题。
解决方案是让您的 View 对象实现一个接口(interface)(接口(interface)隔离原则),如 IDialog
或 IUiDisplay
(根据您想要的抽象程度选择名称),并让您的命令依赖于该接口(interface),而不是直接的具体类型,如下所示:
在 View 中:
public class CreateCategoryView : ..., IUiDisplay
{
...
}
在 View 模型中:
public interface IUiDisplay
{
void Show();
}
public class CreateCategoryCommand : ICommand
{
public CreateCategoryCommand(IUiDisplay uiDisplay) {
if(display == null) throw new ArgumentNullException("uiDisplay");
this.display = uiDisplay;
}
private readonly IUiDisplay display;
...
}
现在,您的命令不再直接依赖于来自更高层的具体(因此它现在是可模拟和可测试的!)。现在您可以让您的 DI/IOC 将命令依赖关系解析为您要注入(inject)的特定 View 类。 (我个人会在命令中注入(inject)一个 View 工厂,并且只懒惰地创建 View ,但这是一个不同的讨论)。
一个相关的注释 - 如果您通过直接让命令实现
ICommand
来实现命令,然后你会重复很多次(DRY)。我的建议是创建一个实现 CommandBase
要求的抽象基类(ICommand
或其他东西) .你会发现从它派生的所有命令只会覆盖Execute()
。有时 CanExecute()
.这使您不必在每个命令中实现事件(和引发事件的代码),并且在许多情况下使您不必实现 CanExecute
因为大多数命令只返回 true
.
关于c# - 这是使用命令模式的正确方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29036977/