【问题标题】:How to use WPF controls with Simple Injector dependencies如何使用带有简单注入器依赖项的 WPF 控件
【发布时间】:2015-08-31 08:34:59
【问题描述】:

我想在必须将资源注入 GUI 控件的情况下使用依赖注入。因为那可能是错误的地方,所以我有一些理由在这里而不是在视图模型中这样做(例如,我需要窗口句柄等)。

构造函数参数注入似乎是首选方式。你们大多数人都知道 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)
   {
   ...
   }
...
}

【问题讨论】:

  • 查看集成指南here
  • 我看到了。但这能解决我的意图吗?实际上我有第二个窗口要填充。但是由于我无法从 XAML 实例化控件,我是否必须放弃我的 XAML? (因为缺少 0-ctor)
  • 您是否有一些代码不工作或者这是一个理论问题?
  • 你使用某种 MVVM 框架吗?
  • 没有 MVVM 框架(还没有真正看过)。这个问题非常具体 - 使用暂存示例它不会编译,但我找不到保留我的 xaml 的方法(可以做 ContentPresenter+Style,但是提出了一个问题)。

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


【解决方案1】:

不仅要使用SOLID 设计原则构建您的视图模型,而且还要在您的视图中执行此操作,这是一种很好的做法。用户控件的使用可以帮助您解决这个问题。

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

SRP 因为您的用户控件需要的所有依赖项都必须注入到使用窗口/视图中,而该视图可能不需要(所有)这些依赖项。

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

使用用户控件,您可以像编写其他类(如服务、命令和查询处理程序等)一样编写视图。在依赖注入方面,编写应用程序的位置是 composition root

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

Caliburn Micro 这样的 MVVM 工具通常使用 contentcontrols 来注入带有自己的视图模型的用户控件视图(阅读: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 工具会使用 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 的出色回答添加一点评论是,我在使用注入“MainWindows”的用户控件(它们只是内容控件的外壳,而不是用户控件)方面取得了巨大的成功。内容控件允许注入几乎任何东西,但通过分离关注点“此用户控件仅执行此...或那个”并添加多个控件,甚至可以加入到另一个用户控件中,您可以选择和选择您想要的部分希望在运行时使用基于用户交互。棱镜做到了这一点。用户控件是视图 DI 的优秀容器。
  • @johnpeters 这正是我使用用户控件的方式,也是尽最大努力实现我的答案的结果。使用 caliburn 和简单的注入器,您甚至可以注入具有运行时绑定的单个或封闭的通用特定视图的开放通用“UserControlViewModels”。可能性几乎是无穷无尽的。
  • 那是一篇很棒的帖子,它基本上使我的问题得到了“否”的回答。基本上我所期望的 - 仍然存在并且仍然存在希望。但是您说 ContentControl 也是在 UI 端进行组合的实际钩子。我已经看到了这一点,现在它在我看来是要走的路。但是我的问题仍然没有解决,因为我的问题有点滥用 WPF。我确实在内部使用 WinFormsHost 和一个自己的、更受限制的对象层次结构。因此 ContentControl 是不可能的,同时 WPF-DC 边界也适用。似乎我必须放弃我的 XAML 方法。
  • +1 用于按预期方式使用 WPF 和 MVVM。没有解决我的问题,但提示我将 ContentControl 的 Content 属性绑定到视图模型,然后使用资源中的 DataTemplate 将所需视图与视图模型相关联。
【解决方案2】:

这可能被认为是一种反模式 - 在多个层面上 - (有关详细信息,请参阅 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>();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 1970-01-01
    • 1970-01-01
    • 2012-08-18
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    相关资源
    最近更新 更多