How to apply dependency injection to UserControl views while saving the constructor?

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

I do not use events (but I am ready if this can solve my problem), so my View classes look like this:

 public class StatisticsForm : Form, IStatisticsView { public StatisticsForm() { InitializeComponent(); } [Inject] public StatisticsViewPresenter Presenter { private get; set; } } 

FROM

 kernel.Bind<StatisticsPresenter>().ToSelf().InSingletonScope(); kernel.Bind<IStatisticsView>().To<StatisticsForm>(); kernel.Get<IStatisticsView>(); 

it creates a form, creates a presenter, and then enters the presenter in the Presenter property. All peachy. (Except the presenter with a singleton region is any thought on a better way to do this? Maybe just manually enter the presenter in the Presenter property view inside the presenter constructor: this.view.Presenter = this).

But if I go to StatisticsForm in StatisticsUserControl and drag it to my MainForm, it will not be nested in MainForm from Ninject, it will just be new'd from the constructor. Here I see three solutions:

1) Do not use UserControls and just use one giant form that implements these multiple views (eww);

2) Injecting UserControls into my form and lose support for Designer;

3) Your decision! :)

+6
c # dependency-injection ninject user-controls windows-forms-designer
source share
3 answers

This is certainly an interesting area, if I say, research. We have created a solution in which we place user controls in a common form.

Our general form is not intended for use with the Designer. Using the code, we add the selected user control to the form dynamically.

For other frameworks, you can look at Prism / Composite in the Microsoft Patterns and Practices group. Here, the article discusses extensions for WinForms.

+2
source share

My approach to using Ninject with forms, usercontrols, and designer:

  • Use factories to create forms (also for custom controls if you create some controls dynamically)
  • for usercontrols and forms save constructors without parameters and use property nesting
  • add an Activation Strategy to the kernel, which checks that ninject has just created a form or user control. If so, the activation strategy iterates over the controls in the Controls UserControl (or in the form) and calls Kernel.Inject (UserControl) for each user control. (Activation strategy - this is some kind of ninject code that is executed after he entered the object)

You can use the constructor and have forms and usercontrols with dependencies injected through Ninject.

The only drawback is that you need to use property injection instead of constructor injection for usercontrols (and forms)

 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; } }); } } } 

Build a kernel and add an activation strategy

 var kernel=new StandardKernel(new CommonMajiicNinjectModule()); kernel.Components.Add<IActivationStrategy, WindowsFormsStrategy>(); 

kernel extensions to iterate over child controls

 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 ); } } } } 
+2
source share

I recently created some reusable UserControls with attachments. Since IoC Container not used to create these UserControl s, obviously, it cannot automatically enter dependencies.

My Solution is a base class that allows at least property embedding. Constructor injection is not supported because a parameterless constructor is used to create these instances.

 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); } } 

To get it to work, you need to install Kernel once. This is usually somewhere inside your program.cs (WinForms) or App.xaml.cs (WPF)

 IocKernel = new StandardKernel(); // typically a static member NinjectUserControl.Kernel = IocKernel; IocKernel.Load(new Module()); // loading modules // .. Create MainForm or whatever 

To use, simply inherit NinjectUserControl , and then let the kernel NinjectUserControl your dependencies through the Injection property:

 [Inject] public IService Service { private get; set; } 

note that these dependencies are not available inside the constructor.

+1
source share

All Articles