我有一个简单的WPF应用程序,它利用Unity Framework进行依赖注入。目前,我正在尝试简化我在MVVM模式实现中的视图之间导航的方法;但是,Stack Overflow中的许多示例都没有考虑依赖注入警告。
我有两个完全独立的观点。
其中一个,Main
充当加载内容的主窗口(非常典型;消除了不必要的内容):
<Window x:Class="Application.UI.Main">
<Grid Background="White">
<ContentControl Content="{Binding aProperty}"/>
</Grid>
</Window>
构造函数通过构造函数注入接收ViewModel(同样,非常简单):
public partial class Main
{
private MainViewModel _mainViewModel;
public Main (MainViewModel mainViewModel)
{
InitializeComponent();
this.DataContext = _mainViewModel = mainViewModel;
}
}
然后我有一个UserControl
,Home
,我想要&#34;导航&#34;主窗口(即设置ContentControl
。它的构造函数也通过构造函数注入接收ViewModel,方式与Main
相同。它同样简单:
public Home(HomeViewModel homeViewModel)
{
InitializeComponent();
// Set Data Context:
this.DataContext = homeViewModel;
}
这里的主要问题是我希望在保持纯MVVM实现的同时启用基于构造函数的注入。
我在MVVM的View-first阵营中,你可以找到一个好的discussion in these comments。
我看到了一些基于导航的服务的暗示;但是,我不确定这是否能维持MVVM所争夺的关注点分离。 DataTemplates
需要不带参数的View构造函数,并且我读过对DataTemplates的批评,认为ViewModels不应该参与Views的实例化。
This solution(在我看来)是完全错误的,因为ViewModel意识到它的View并依赖于ViewModel实例的服务,这使得真正的依赖注入能够解决ViewModel和View依赖关系。在此MSDN article中使用RelayCommand
时,此问题非常明显。
维护对Main
视图的全局,类似单一的引用的导航服务是否最有意义? Main
视图是否可以公开方法,例如:
public void SetContent(UserControl userControl) { //... }
然后由该服务访问?
答案 0 :(得分:1)
这是我对另一位作者提供的解决方案实施背后的动机的阐述。我不提供代码,因为链接文章提供了很好的代码示例。这些当然是我的意见,但也代表了我对该主题的研究的合并
Rachel Lim写了一篇很棒的文章 Navigation with MVVM ,它描述了如何充分利用WPF的DataTemplate
来解决MVVM导航带来的挑战。 Lim的方法提供了“最佳”解决方案,因为它大大减少了对任何框架依赖性的需求;然而,确实没有“伟大”的方法来解决这个问题。
一般来说,对Rachel方法的最大反对意见是View Model然后负责 - 或“定义” - 它与View的关系。对于Lim的解决方案,出于两个原因这是一个小问题(并没有进一步证明其他错误的架构决策,稍后描述):
1。)除了XAML文件之外,DataTemplate
关系不是强制执行的,即视图模型本身从不直接知道他们的视图,反之亦然,所以甚至我们的View的构造函数进一步简化,例如Home
类构造函数 - 现在不需要对View Model的引用:
public Home()
{
InitializeComponent();
}
2。)由于关系在其他地方表达,特定视图和视图模型之间的关联很容易改变。
应用程序应该能够在没有指定视图的情况下充分运行(模拟域)。这种理想源于努力最好地解耦应用程序的支持体系结构,并促进SOLID编程原则的进一步应用,特别是依赖注入。
XAML文件 - 不是第三方依赖容器 - 成为解决视图和视图模型之间关系的关键点(因此,它直接与OP相矛盾)。
应该将应用程序设计为完全不知道其容器,并且 - 甚至更好 - 任何有关服务依赖性的特定于实现的信息。这使我们能够做的是“分配”(注入)服务,这些服务需要通过某个合同(接口)来构成构成我们应用程序功能的各种类。
这给我们留下了两个“好设计”的标准:
第二点至关重要,并且通常是一个破坏的“规则”。 “破除规则”是原始帖子背后的灵感。
许多应用程序对导航问题的响应是注入一个包装的依赖注入容器,然后用它来调用实现类来解析依赖关系。现在,班级知道容器,更糟糕的是,对于执行其操作所需的具体细节有更多,更具体的知识(有些人可能认为更难以维护)。
视图,视图模型或模型方面的依赖关系解析容器的任何知识都是反模式(您可以在其他地方阅读有关该语句的理由的更多信息)。
依赖于依赖注入的编写良好的应用程序可以在没有依赖注入框架的情况下运行,即您可以手动从手写引导程序中解析依赖关系(尽管这需要大量细致的工作)。
Lim的解决方案使我们能够从实现中“不需要”对容器的引用。
我们应该留下的是看起来像的构造函数:
// View:
public Home() { //... }
// View Model
public HomeViewModel (SomeModelClass someModel, MaybeAService someService)
如果一个目标是模块化和可重用性,那么以上就可以实现这一目标。我们可以继续通过确保那些传入的依赖关系是通过接口实现合同来进一步抽象出来。