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

时间:2015-08-31 08:34:59

标签: c# wpf dependency-injection simple-injector

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

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

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

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

方案示例:

<Grid>
 <gg:WhateverResourceNeedingViewer ItemSource={Binding Items}/>
</Grid>

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

2 个答案:

答案 0 :(得分:5)

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

如果技术上可行的话,您建议的方法的缺点是此设计将违反SRPOCP

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

和OCP因为你从usercontrol中添加或删除了一个依赖项,你还需要在使用窗口/视图中添加或删除它。

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

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

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

这看起来像这样:

public interface IViewModel<T> { }

public class MainViewModel : IViewModel<Someclass>
{
    public MainViewModel(IViewModel<SomeOtherClass> userControlViewModel)
    {
        this.UserControlViewModel = userControlViewModel;
    }

    public IViewModel<SomeOtherClass> UserControlViewModel { get; private set; }
}

public class UserControlViewModel : IViewModel<SomeOtherClass>
{
    private readonly ISomeService someDependency;

    public UserControlViewModel(ISomeService someDependency)
    {
        this.someDependency = someDependency;
    }
}

MainView的XAML:

// MainView
<UserControl x:Class="WpfUserControlTest.MainView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <ContentControl Name="UserControlViewModel" />
    </Grid>
</UserControl>

// UserControl View
<UserControl x:Class="WpfUserControlTest.UserControlView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <TextBlock Text="SomeInformation"/>
    </Grid>
</UserControl>

结果将是MainView显示在该窗口的DataContext设置为MainViewModel的窗口中。内容控件将填充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...
}

答案 1 :(得分:0)

这可能被认为是一种反模式-从多个层面上来说(请参阅Ric的答案以获取详细信息),但是如果您只是想使它起作用,希望务实并有一个简单的用例,我建议静态DI解析器。这对于遗留组件或诸如此类受基础架构约束的情况非常有用。

/// <summary>
///     Provides static resolution of Simple Injector instances.
/// </summary>
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<T>()
    {
        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<DepResource>();
}