注入依赖项到WPF行为的清晰方法

5
我需要创建一个自定义的WPF行为,并在构造函数中注入一些依赖项。我不能使用构造函数注入,因为我需要在XAML中应用它,WPF会使用默认构造函数创建实例。一种方法是在默认构造函数或服务定位器中使用容器,但我不想使用这些方法。我可以在属性上使用[Import]并使用Container.SatisfyImportOnce,但这不是很好的方法,因为我仍然会在构造函数中使用容器。有什么建议吗?
我正在我的WPF项目中使用MEF进行依赖项注入。
好的方法:它应该是可测试的,我应该能够注入依赖项,并且应该在WPF XAML中实例化。

1
不够具体。我认为您需要在定义中明确“整洁的方式”的含义。 - cscmh99
1
我认为我理解你的问题了。虽然可能可以像依赖注入书籍中所说的那样去处理,但我建议不要过于复杂化问题,仅仅因为它更加“整洁”。在某些情况和API中,你不能简单地像使用自己的类(构造函数注入)一样构建对象图。归根结底,许多这些trick的依赖注入解决方案只是为了实现“正确”的依赖注入,而依赖注入不是目标,而是一种工具。 - Peter Porfy
这确实是视图的一部分,因为我们正在使用工作区管理器管理工作区并跟踪窗口,我们需要管理器将每个窗口注册到工作区管理器中。此外,在某个地方,我需要将小部件的大小保存到我的应用程序设置中,并且我希望在多个地方使用相同的行为。用户可能会说我们希望将UI设置持久化到数据库中,我可能需要您的建议。 - Rajnikant
我怀疑你的整体架构设计存在问题。行为是视图的一部分,在MVVM中用于帮助视图显示非应用程序特定数据。让你的行为使用应用程序特定的数据结构本质上与代码后台相同,并且通常是您的视图模型没有正确执行其工作的很好的指示。其中一个很好的例子是单元测试:DI使在VM中进行测试成为可能,但如果您只是要在视图所需的行为中使用它,则是浪费的努力! - Mark Feldman
1个回答

8

如您所知,Wpf不支持构造函数注入,但可以在Xaml中定义属性注入。我认为一种“简洁”的解决方案是使用 MarkupExtension,例如:

public sealed class Resolver : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var provideValueTarget = (IProvideValueTarget)serviceProvider
            .GetService(typeof(IProvideValueTarget));

        // Find the type of the property we are resolving
        var targetProperty = provideValueTarget.TargetProperty as PropertyInfo;

        if (targetProperty == null)
        {
            throw new InvalidProgramException();
        }

        // Find the implementation of the type in the container
        return BootStrapper.Container.Resolve(targetProperty.PropertyType);
    }
}

如果您的行为如下:

public sealed class InitialiseWebBrowser : Behavior<MyVM>
{
    public IQueryHandler<Query.Html, string> HtmlQueryHandler { private get; set; }

    // ...
}

我们可以在Xaml中这样配置属性注入:

我们可以在Xaml中这样配置属性注入:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    xmlns:inf="clr-namespace:abc"
    xmlns:behaviours="clr-namespace:def"
    x:Class="MyVM">
    <i:Interaction.Behaviors>
        <behaviours:InitialiseWebBrowser HtmlQueryHandler="{inf:Resolver}"/>
    </i:Interaction.Behaviors>
Resolver MarkupExtension将会:
  • 分析其定义属性的类型(在本例中,HtmlQueryHandler是一个IQueryHandler<Query.Html, string>
  • 从所使用的任何基础容器/解析器中解析出该类型
  • 将其注入到行为中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接