You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在作为页面的UserControl中为内部Control实现Dependency Injection(StructureMap)

在WPF里给继承自Control的自定义控件实现依赖注入确实有点棘手——毕竟XAML实例化控件默认只会调用无参构造函数,没法直接用构造注入那套。结合你参考的StructureMap仓库,我给你分享几个可行的方案,你可以根据自己的场景来选:

方案1:通过服务定位器(结合StructureMap)实现属性注入

这是最直接的方式,适合已经在XAML里声明了CustomControl的场景。首先得确保你的DI容器在应用中全局可访问,比如在App类里存一个静态实例:

public partial class App : Application
{
    public static IContainer Container { get; private set; }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        // 初始化StructureMap容器,注册你的服务
        Container = new Container(cfg =>
        {
            cfg.For<IMyService>().Use<MyServiceImpl>();
            // 其他需要的服务注册...
        });
        // 初始化主窗口等逻辑
    }
}

然后在你的CustomControl里,借助Loaded事件从容器中获取服务:

public class CustomControl : Control
{
    public IMyService MyService { get; private set; }

    public CustomControl()
    {
        Loaded += CustomControl_Loaded;
    }

    private void CustomControl_Loaded(object sender, RoutedEventArgs e)
    {
        // 从全局容器获取服务实例
        MyService = App.Container.GetInstance<IMyService>();
        // 现在就可以用MyService执行业务逻辑了
    }
}

注意:服务定位器模式虽然简单,但会让控件和DI容器产生耦合,后续单元测试会稍微麻烦些。如果你的控件逻辑复杂,建议考虑后面的方案。

方案2:使用附加属性实现注入(更贴合WPF风格)

这种方式能避免直接引用全局容器,通过附加属性来完成服务注入,代码更整洁。首先创建一个附加属性类:

public static class DependenciesInjector
{
    public static readonly DependencyProperty InjectServicesProperty =
        DependencyProperty.RegisterAttached(
            "InjectServices",
            typeof(bool),
            typeof(DependenciesInjector),
            new PropertyMetadata(false, OnInjectServicesChanged));

    public static bool GetInjectServices(DependencyObject obj)
    {
        return (bool)obj.GetValue(InjectServicesProperty);
    }

    public static void SetInjectServices(DependencyObject obj, bool value)
    {
        obj.SetValue(InjectServicesProperty, value);
    }

    private static void OnInjectServicesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue && d is CustomControl customControl)
        {
            // 从全局容器获取服务并注入到控件
            var myService = App.Container.GetInstance<IMyService>();
            customControl.MyService = myService;
        }
    }
}

接着在XAML里给CustomControl设置这个附加属性即可:

<local:CustomControl local:DependenciesInjector.InjectServices="True" />

这样控件加载时,附加属性会自动完成服务注入,和WPF的绑定系统更契合。

方案3:让容器创建CustomControl(支持构造注入)

如果你的CustomControl不需要在XAML里硬编码,而是可以通过ViewModel控制创建,那优先用这个方案——完全符合DI的构造注入原则。

首先在容器里注册CustomControl:

Container = new Container(cfg =>
{
    cfg.For<IMyService>().Use<MyServiceImpl>();
    cfg.For<CustomControl>().Use<CustomControl>(); // 注册自定义控件
});

然后在UserControl的ViewModel里,直接注入CustomControl(或者通过容器解析):

public class MyPageViewModel
{
    public CustomControl CustomControlInstance { get; }

    // 构造注入CustomControl
    public MyPageViewModel(CustomControl customControl)
    {
        CustomControlInstance = customControl;
    }

    // 或者通过容器解析(如果不想直接注入控件实例)
    // public MyPageViewModel(IContainer container)
    // {
    //     CustomControlInstance = container.GetInstance<CustomControl>();
    // }
}

最后在UserControl的XAML里,用ContentControl绑定这个实例:

<ContentControl Content="{Binding CustomControlInstance}" />

这种方式让控件的依赖在创建时就被注入,测试起来更方便,也完全遵循MVVM和DI的设计原则。

结合你参考仓库的额外建议

你参考的仓库用StructureMap注入ViewModel和视图,你可以把CustomControl的注册整合到现有的容器配置里(比如仓库的Bootstrapper类),让整个应用的DI配置保持统一。

另外,如果CustomControl的逻辑复杂,建议把业务逻辑抽离到对应的ViewModel中,控件只负责UI展示和绑定,这样DI的重心可以放在ViewModel上,代码结构会更清晰。

内容的提问来源于stack exchange,提问作者Dominic Boexler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:12:14