c# - 这是使用命令模式的正确方法吗?

标签 c# wpf mvvm dependency-injection command-pattern

与其他问题相关: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)。

最后,这将呈现 中继命令类无用...

是这样吗?

最佳答案

首先,我同意 RelayCommandDelegateCommand等是执行违反 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)隔离原则),如 IDialogIUiDisplay (根据您想要的抽象程度选择名称),并让您的命令依赖于该接口(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/

相关文章:

c# - 放弃 C# 7.0 中功能的重要性?

WPF 转换器和 ObservableCollections

c# - 将 UI 从 CLI 更改为 WPF

c# - 评估元素 'System.Windows.Controls.TextBlock' 上的 ThemeStyle 属性时发现循环引用

wpf - 如何强制显示忙碌指示符? (WPF)

c# - Xamarin.Forms ViewModel 在页面加载时访问了两次

c# - 具有数据库事务的工作单元模式

c# - 如何在 iOS 上访问用户的 MKAnnotationView

wpf - Caliburn micro 和 tabcontrol

c# - 创建动态图表工具提示