C# 如何将依赖项注入应用于UserControl视图,同时让设计器满意?

C# 如何将依赖项注入应用于UserControl视图,同时让设计器满意?,c#,dependency-injection,user-controls,ninject,windows-forms-designer,C#,Dependency Injection,User Controls,Ninject,Windows Forms Designer,我不使用事件(但如果它能解决我的问题,我愿意这样做),因此我的视图类如下所示: public class StatisticsViewPresenter { private IStatisticsView view; private Statistics statsModel; public StatisticsViewPresenter(IStatisticsView view, Statistics statsModel) { this.vi

我不使用事件(但如果它能解决我的问题,我愿意这样做),因此我的视图类如下所示:

public class StatisticsViewPresenter
{
    private IStatisticsView view;
    private Statistics statsModel;

    public StatisticsViewPresenter(IStatisticsView view, Statistics statsModel)
    {
        this.view = view;
        this.statsModel = statsModel;
    }
}

kernel.Bind().ToSelf().InSingletonScope();
kernel.Bind().To();
Get();
它构建表单,构建presenter,然后将presenter注入presenter属性。一切都很美好。(除了单一作用域的presenter之外——还有什么更好的方法吗?也许只需手动将presenter注入presenter构造函数中视图的presenter属性:this.view.presenter=this)

但如果我将StatisticsForm转换为StatisticsUserControl并将其拖放到我的MainForm上,Ninject并没有将其注入MainForm,它只是由设计师进行了新的设计。我在这里看到三种解决方案:

1) 不要使用UserControls,只使用一个实现这些多视图的巨型表单(eww)

2) 将UserControls注入我的表单并失去设计器支持


3) 您的解决方案!:)

应该说,这的确是一个有趣的研究领域。我们已经为自己制定了一个解决方案,其中以通用形式托管用户控件

我们的通用表单不适用于设计器。通过代码,我们将选择的用户控件动态添加到表单中


对于其他框架,您应该从Microsoft模式与实践组了解。讨论WinForms的扩展。

我将Ninject与表单、用户控件和设计器一起使用的方法是:

  • 使用工厂来创建表单(如果您以手动方式创建一些控件,则也适用于usercontrols)
  • 对于usercontrols和表单,保持构造函数不带参数,并使用属性注入
  • 向内核添加一个用于检查ninject是否刚刚创建了表单或usercontrol的。如果是这种情况,激活策略将迭代UserControl(或表单)的controls属性中的控件,并为每个UserControl调用Kernel.Inject(UserControl)。(激活策略是ninject在注入对象后执行的代码)
您可以使用设计器,并通过Ninject注入依赖项的表单和用户控件

唯一的缺点是必须对usercontrols(和表单)使用属性注入而不是构造函数注入

namespace Majiic.Ninject
{
公共类窗口窗体策略:激活策略
{
//Activate在Kernel.Inject之后调用
//即使对于不是由Ninject创建的对象也是如此
//避免在同一嵌套控件中多次“注入”
//我们把这面旗设为假。
私有bool _activatingControls=false;
公共覆盖无效激活(IContext上下文、InstanceReference引用)
{
参考。iInstanceIS(uc=>
{
如果(!\u激活控制)
{
Trace.TraceInformation(“在{0}类型的用户控件中激活.注入依赖项”,uc.GetType());
_激活控件=真;
context.Kernel.injectdownantof(uc);
_激活控制=假;
}
});
reference.iInstanceIS(表单=>
{
如果(!\u激活控制)
{
Trace.TraceInformation(“以类型{0}的形式激活.注入依赖项”,Form.GetType());
_激活控件=真;
context.Kernel.injectDegeneratof(form);
_激活控制=假;
}
});
}
}
}
创建内核并添加激活策略

namespace Majiic.Ninject
{
public class WindowsFormsStrategy : ActivationStrategy
{
    // Activate is called after Kernel.Inject
    //even for objects not created by Ninject
    //To avoid multiple "injections" in the same nested controls
    //we put this flag to false.
    private bool _activatingControls = false;
    public override void Activate(IContext context, InstanceReference reference)
    {
        reference.IfInstanceIs<UserControl>(uc =>
        {
            if (!_activatingControls)
            {
                Trace.TraceInformation("Activate. Injecting dependencies in User control of type {0}", uc.GetType());
                _activatingControls = true;
                context.Kernel.InjectDescendantOf(uc);
                _activatingControls = false;
            }
        });
        reference.IfInstanceIs<Form>(form =>
        {
            if (!_activatingControls)
            {
                Trace.TraceInformation("Activate. Injecting dependencies in Form of type {0}", form.GetType());
                _activatingControls = true;
                context.Kernel.InjectDescendantOf(form);
                _activatingControls = false;
            }
        });
    }


}
}
var-kernel=new-StandardKernel(new-CommonMajiicNinjectModule());
cornel.Components.Add();
迭代子代控件的内核扩展

var kernel=new StandardKernel(new CommonMajiicNinjectModule());
kernel.Components.Add<IActivationStrategy, WindowsFormsStrategy>();
namespace Majiic.Ninject
{
静态公共类WinFormsInstanceProviderAux
{
静态公共无效注入(此IKernel内核,ContainerControl ContainerControl)
{
var childrenControls=containerControl.Controls.Cast();
foreach(ChildrenControl中的var控制)
{
InjectUserControlsOf(内核,控件);
}
}
静态私有void InjectUserControlsOf(此IKernel内核,控件)
{
//只有用户控件可以具有定义为n-inject-able的属性
if(控件为UserControl)
{
Trace.TraceInformation(“在{0}类型的用户控件中注入依赖项”,Control.GetType());
内核注入(控制);
}
//非用户控件可以有属于用户控件的子控件,并且应该是n注入的
var childrenControls=control.Controls.Cast();
foreach(childrenControls中的var childControl)
{
InjectUserControlsOf(内核、子控件);
}
}
}
}

我最近创建了一些可重用的
用户控件
,需要注入依赖项。由于
IoC容器
不用于创建那些
UserControl
s,显然他无法自动注入依赖项

我的解决方案是,一个至少支持属性注入的基类。不支持构造函数注入,因为使用无参数构造函数创建这些实例

namespace Majiic.Ninject
{
static public class WinFormsInstanceProviderAux
{
    static public void InjectDescendantOf(this IKernel kernel, ContainerControl containerControl)
    {
        var childrenControls = containerControl.Controls.Cast<Control>();
        foreach (var control in childrenControls )
        {
            InjectUserControlsOf(kernel, control);
        }
    }

    static private void InjectUserControlsOf(this IKernel kernel, Control control)
    {
        //only user controls can have properties defined as n-inject-able
        if (control is UserControl)
        {
            Trace.TraceInformation("Injecting dependencies in User Control of type {0}", control.GetType());
            kernel.Inject(control);
        }
        //A non user control can have children that are user controls and should be n-injected
        var childrenControls = control.Controls.Cast<Control>();
        foreach (var childControl in childrenControls )
        {
            InjectUserControlsOf(kernel, childControl );
        }
    }
}
}
要让它工作,您需要设置内核一次。通常,这在
program.cs
(WinForms)或
App.xaml.cs
(WPF)中的某个位置

要使用,只需继承
NinjectUserControl
,然后让内核通过属性注入注入依赖项:

IocKernel = new StandardKernel(); // typically a static member
NinjectUserControl.Kernel = IocKernel;
IocKernel.Load(new Module()); // loading modules
// .. Create MainForm or whatever
请注意,这些依赖项在构造函数中是不可访问的


最终的解决方案是什么?我也在寻找一种在设计器中使用UserControls和依赖注入的方法。如果你找到了一个可行的解决方案,请告诉我们。谢谢基本上,我使用设计器将用户控件拖到窗体上,并公开这些控件
namespace Majiic.Ninject
{
static public class WinFormsInstanceProviderAux
{
    static public void InjectDescendantOf(this IKernel kernel, ContainerControl containerControl)
    {
        var childrenControls = containerControl.Controls.Cast<Control>();
        foreach (var control in childrenControls )
        {
            InjectUserControlsOf(kernel, control);
        }
    }

    static private void InjectUserControlsOf(this IKernel kernel, Control control)
    {
        //only user controls can have properties defined as n-inject-able
        if (control is UserControl)
        {
            Trace.TraceInformation("Injecting dependencies in User Control of type {0}", control.GetType());
            kernel.Inject(control);
        }
        //A non user control can have children that are user controls and should be n-injected
        var childrenControls = control.Controls.Cast<Control>();
        foreach (var childControl in childrenControls )
        {
            InjectUserControlsOf(kernel, childControl );
        }
    }
}
}
public class NinjectUserControl : UserControl
{

    // Generally this is considered to be a bad practice, 
    //however I didn't find any better way. If you do, please share :)
    public static IKernel Kernel { private get; set; } 

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);
        RequestActivation(Kernel);
    }

    protected virtual void RequestActivation(IKernel kernel)
    {
        kernel?.Inject(this);
    }
}
IocKernel = new StandardKernel(); // typically a static member
NinjectUserControl.Kernel = IocKernel;
IocKernel.Load(new Module()); // loading modules
// .. Create MainForm or whatever
[Inject]
public IService Service { private get; set; }