如何使用Simple Injector依赖项的WPF控件

我想在我必须将资源注入GUI控件的场景中使用dependency injection。 因为这可能是错误的地方,我有一些理由在这里做而不是在视图模型中(例如,我需要Window句柄等)。

构造函数参数注入似乎是首选方式。 正如大多数人所知,WPF控件必须具有无参数构造函数,否则XAML不起作用,对于当前场景,我喜欢保留我的XAML,因为它包含一些名称注册和绑定。

那么:我如何在WPF + XAML场景中使用构造函数-DI(如果可能的话,在Simple Injector的情况下)?

是否存在标记扩展,或者XAML解析器是否可以成为Container-Aware并接受具有参数的构造函数作为控件?

方案示例:

   

和:

 public class WhateverResourceNeedingViewer : ItemsControl { public WhateverResourceNeedingViewer(Dep1 d, DepResource d2) { ... } ... } 

最好不仅使用SOLID设计原则构建视图模型,还要在视图中执行此操作。 usercontrols的使用可以帮助您解决这个问题。

如果技术上可行的话,您建议的方法的缺点是该设计将违反SRP和OCP 。

SRP,因为您的usercontrol需要的所有依赖项必须在使用窗口/视图中注入,而此视图可能不需要(所有)这些依赖项。

和OCP因为每次从usercontrol添加或删除依赖项时,您还需要在使用窗口/视图中添加或删除它。

使用usercontrols,您就可以像编写其他类(如服务,命令和查询处理程序等)一样编写视图。当涉及到依赖项注入时,编写应用程序的位置就是组合根

WPF中的ContentControl都是关于从应用程序中的其他“内容”“组合”您的视图。

像Caliburn Micro这样的MVVM工具通常使用内容控件来注入一个usercontrol视图(读取:xaml,无代码隐藏)和它自己的viewmodel。 事实上,在使用MVVM时,您将从usercontrols类构建应用程序中的所有视图,这是最佳实践。

这看起来像这样:

 public interface IViewModel { } public class MainViewModel : IViewModel { public MainViewModel(IViewModel userControlViewModel) { this.UserControlViewModel = userControlViewModel; } public IViewModel UserControlViewModel { get; private set; } } public class UserControlViewModel : IViewModel { private readonly ISomeService someDependency; public UserControlViewModel(ISomeService someDependency) { this.someDependency = someDependency; } } 

和MainView的XAML:

 // MainView      // UserControl View      

结果是MainView显示在窗口中,该窗口的DataContext设置为MainViewModel 。 contentcontrol将使用UserControlView填充,其DataContext设置为UserControlViewModel类。 这是自动发生的,因为MVVM工具将使用Convention over configuration将视图模型绑定到相应的视图。

如果您不使用MVVM工具但直接在窗口类的代码中注入依赖项,则只需遵循相同的模式即可。 在视图中使用ContentControl,就像上面的示例一样,在窗口的构造函数中注入UserControl (带有包含参数的构造函数)。 然后只需将ContentControl的Content属性设置为UserControl的注入实例。

那看起来像是:

 public partial class MainWindow : Window { public MainWindow(YourUserControl userControl) { InitializeComponent(); // assuming you have a contentcontrol named 'UserControlViewModel' this.UserControlViewModel.Content = userControl; } // other code... } 

这可能被认为是一种反模式 – 在许多层面上 – (详情请参阅Ric的答案),但如果你只是想让这个工作,希望务实并且有一个简单的用例,我建议静态DI解析器。 这对于遗留组件或这样的情况来说非常方便,其中一个受到底层架构的约束。

 ///  /// Provides static resolution of Simple Injector instances. ///  public class ServiceResolver { private Container Container { get; } private static ServiceResolver Resolver { get; set; } public ServiceResolver(Container container) { Container = container; Resolver = this; } public static T GetInstance() { if (Resolver == null) throw new InvalidOperationException($"{nameof(ServiceResolver)} must be constructed prior to use."); return (T) Resolver.Container.GetInstance(typeof(T)); } } 

用法,来自您的示例:

 public WhateverResourceNeedingViewer() { InitializeComponent(); // Resolve view model statically to fulfill no-arg constructor for controls DataContext = ServiceResolver.GetInstance(); }