繁体   English   中英

将Autofac容器传递给WPF UserControl

[英]Pass Autofac container to WPF UserControl

我正在使用autofac来解析WPF应用程序中的Views和ViewModel。 IComponentContext将自动传递到View中。

一个例子:

    public BusinessAuto(int proposedCoverageId, IComponentContext componentContext)
    {
        DataContext = componentContext.Resolve<BusinessAutoViewModel>(new TypedParameter(typeof(Int32), proposedCoverageId));
        InitializeComponent();
    }

在此视图的XAML中,创建了具有自己的ViewModel的UserControl。 一个例子:

<userControl:AdditionalCoveragesControl Margin="0,10"/>

Autofac不会创建UserControl(View是),因此Autofac无法将依赖项注入UserControl的构造函数中。

如何将对IComponentContext的引用转换为在初始View的XAML中声明的UserControl?

我觉得我要么需要Autofac以某种方式创建我的UserControl,我需要恢复到不鼓励的全局静态容器(ick),或者我必须使用DependencyProperty来传递容器(也是ick)。

我不会注入(有效的)你的容器,因为这是一种称为服务定位器的低级控制反转形式,缺点可以通过你当前的情况来总结:你最终需要将容器注入一切。

相反,您需要从“负责创建的这个组件是什么,以及它需要做什么?”的角度来解决问题。

正如Lonni-Loki所提到的,一个选择是注入一个完全形成的控件,但我不同意这一点:如果主视图有责任创建这个子组件,那么它应该创建它 - 但为了促进这个责任,你应该注入主视图与服务/模型/等,然后需要传递或以其他方式用于创建它。 Autofac的工厂方法存根非常适用于此:

例如,如果子视图需要IFooViewModel,则可以使用Func <IFooViewModel <(在上下文中注册的工厂方法)注入容器,然后可以使用它来“按需”提供新包含的视图。

(或Func <arg1,arg2等,IFooViewModel>,根据您的需要)

一个方便的经验法则是在考虑X类时,首先在任何地方“新建”任何东西,然后将其传递给构造函数。 现在看看代码并问自己“如果我想要一个X类的实例,我需要传递什么构造函数?” 那些是你的依赖。

让我们来看一个更实际的例子...说你有这样的结构:

  • 应用程序创建MainWindow

  • MainWindow创建SubView1,需要IMainWindowViewModel

  • SubView1需要ISubView1Model,IFooService

  • SubView1创建SubView2

  • SubView2需要ISubView2Model,IBarService

所以我们的ctors看起来像:

public MainWindow(IMainWindowViewModel viewModel, 
     Func<SubView1> subView1Factory)

public SubView1(ISubView1Model viewModel,
    IFooService fooService,
    Func<IFooService, SubView2> subView2Factory)

public SubView2(
    ISubView2ModelViewModel viewModel, 
    IBarService barService)

在设置容器时,你会有这样的事情:

(注意,我使用了各种IoC容器,因此我的Autofac语法可能会生锈)

var builder = new ContainerBuilder();

// Simple bit, register implementations for viewmodel, services
builder.RegisterType<MainWindowViewModel>.As<IMainWindowViewModel>();
builder.RegisterType<SubView1Model>.As<ISubView1Model>();
builder.RegisterInstance<FooService>.As<IFooService>();

// ok, lemme see if I can remember expression syntax...

// Simple case: 'static' resolution of subview
// We want a func that takes no args and returns us a fully-initialized
//  SubView1
builder.Register<Func<SubView1>>(context =>
{
    // Since all the bits of a subview1 are registered, simply
    // resolve it and return
    var view = context.Resolve<SubView1>();
    return () => view;
});

// Complicated case - lets say this viewmodel depends
// on foo service, which it uses to determine which 
// bar service to use
builder.Register<Func<IFooService, SubView2>>(context =>
{
    // and our view model
    var vm = context.Resolve<ISubView2ViewModel>();

    return (service) =>
    {
        var barService = new BarService(service);
        return new SubView2(vm, barService);
    };
});

光荣(在我看来,仅限于)IoC容器的使用是“你弄清楚如何根据我已经告诉你的东西得到所有的部分” - 否则,你不妨使用手动注射,你在哪里手工传递依赖关系。 也就是说,我们可能复杂的MainWindow构建现在只是:

container.Resolve<MainWindow>();

我希望我没有在代码中输入太多的拼写错误/错误,但我还没有使用Autofac一段时间。

您不应该将componentContext传递给BusinessAuto视图,而应该传递AdditionalCoveragesControl。

public BusinessAuto(int proposedCoverageId, BusinessAutoViewModel vm, AdditionalCoveragesControl view)
{ 
  ...
  DataContext = vm;
  InternalView = view;
}    

XAML:

 <ContentPresenter Content="{Binding InternalView}" Margin="0,10"/>

然后你的所有视图将独立于Autofac,你应该只在容器中注册它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM