如何使用具有简单注入器依赖的WPF控件

本文关键字:依赖 WPF 控件 注入器 简单 何使用 | 更新日期: 2023-09-27 18:10:26

我想在我必须向gui控件注入资源的场景中使用依赖注入。因为这可能是错误的地方,我有一些理由在这里做,而不是在视图模型(例如。我需要窗口句柄之类的)。

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

那么:我如何在WPF+XAML场景中使用构造器di和(如果可能的话,在简单注入器的情况下)?

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

方案的例子:

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

:

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

如何使用具有简单注入器依赖的WPF控件

不仅使用SOLID设计原则构建视图模型,而且在视图中也这样做是很好的实践。使用usercontrols可以帮助您实现这一点。

你建议的方法的缺点是,如果技术上可能的话,这个设计将违反SRP和OCP。

SRP,因为你的用户控件需要的所有依赖都必须注入到消费窗口/视图中,而这个视图可能不需要(全部)这些依赖。

和OCP,因为每当你从用户控件中添加或删除依赖项时,你也需要从消费窗口/视图中添加或删除它。

有了usercontrols,你就可以像组合服务、命令和查询处理程序等类一样组合视图。当涉及到依赖注入时,组合应用程序的位置是组合根

WPF中的ContentControls都是关于从应用程序中的其他"内容"中"组合"你的视图。

像Caliburn Micro这样的MVVM工具通常使用contentcontrols来注入带有自己视图模型的usercontrol视图(读取:xaml,没有后面的代码)。事实上,在使用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工具将使用配置约定将视图模型绑定到相应的视图。

如果你不使用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解析器。这对于遗留组件或受底层体系结构约束的情况非常方便。

/// <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>();
}