How to apply dependency injection to UserControl views while keeping Designer happy? - c#

public class StatisticsViewPresenter
{
private IStatisticsView view;
private Statistics statsModel;
public StatisticsViewPresenter(IStatisticsView view, Statistics statsModel)
{
this.view = view;
this.statsModel = statsModel;
}
}
I don't use events (but am willing to if it 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;
}
}
With
kernel.Bind<StatisticsPresenter>().ToSelf().InSingletonScope();
kernel.Bind<IStatisticsView>().To<StatisticsForm>();
kernel.Get<IStatisticsView>();
it builds up the Form, builds up the presenter, then injects the presenter into the Presenter property. Everything's peachy. (Except for that singleton-scoped presenter--any thoughts on a better way to do that? Perhaps just manually inject the presenter into the view's Presenter property inside the presenter's constructor: this.view.Presenter = this).
But if I turn StatisticsForm into StatisticsUserControl and drag-drop it onto my MainForm, it's not being injected into MainForm by Ninject, it's simply being new'd by the Designer. I see three solutions here:
1) Don't use UserControls and just use one giant form that implements these multiple views (eww);
2) Inject UserControls into my form and lose Designer support;
3) Your solution! :)

My approach to use Ninject with forms, usercontrols and the designer is:
Use factories to create the forms (also for the usercontrols if you create some controls dinamically)
for the usercontrols and the forms keep the constructors without parameters and use property injection
add an Activation strategy to the kernel that check if ninject has just created a form or a usercontrol. If that is the case, the activation strategy iterates over the controls in the Controls property of the UserControl (or the Form) and calls Kernel.Inject(UserControl) for each usercontrol. (An Activation strategy is some code ninject executes after it has injected an object)
You can use the designer and have forms and usercontrols with dependencies injected via Ninject.
The only drawback is that you have to use property injection instead of constructor injection for the usercontrols (and the 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;
}
});
}
}
}
Create the kernel and add the Activation Strategy
var kernel=new StandardKernel(new CommonMajiicNinjectModule());
kernel.Components.Add<IActivationStrategy, WindowsFormsStrategy>();
kernel extensions to iterate over descendents 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 );
}
}
}
}

This is certainly an interesting area of, should I say, research. We've made ourselves a solution where we host user controls in a generic form.
Our generic form is not intended for use with the Designer. Through code we add the chosen user control to the Form dynamically.
For other frameworks you should look at Prism/Composite from the Microsoft Patterns & Practices group. Here's an article discussing extensions for WinForms.

I recently created some reuseable UserControls with the need of Dependencies to inject. As the IoC Container isn't used to create those UserControls, obviously he cannot automatically inject the Dependencies.
My Solution is, a Base-Class to enable at least Property Injection. Constructor Injection is not supported, as a parameterless Constructor is used to create those 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 set the Kernel once. Typically this is 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 Inject your dependencies via Property Injection:
[Inject]
public IService Service { private get; set; }
please note, that those dependencies are not accessible inside the Constructor.

Related

Access container outside of constructor

With Unity, I can inject various controls/interfaces via constructor like following:
private readonly IEmployeeRepository _employeeRepository;
public EmployeeView_EmployeeListViewModel(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
However, I need to access the specific control (let's say the one used in an example) outside of the constructor (I cannot edit constructor).
Is there a way, how to do it?
EDIT
more info - I have a DataForm, which allows users to do simple CRUD operations on their DataGrid (simple edit form). This control is from Telerik inc. and therefore it's commands class looks like following:
public class CustomDataFormCommandProvider : DataFormCommandProvider
{
public CustomDataFormCommandProvider():base(null)
{
}
protected override void MoveCurrentToNext()
{
if (this.DataForm != null)
{
this.DataForm.MoveCurrentToNext();
this.DataForm.BeginEdit();
}
}
protected override void MoveCurrentToPrevious()
{
if (this.DataForm != null)
{
this.DataForm.MoveCurrentToPrevious();
this.DataForm.BeginEdit();
}
}
protected override void CommitEdit()
{
if (this.DataForm != null && this.DataForm.ValidateItem())
{
this.DataForm.CommitEdit();
}
}
protected override void CancelEdit()
{
if (this.DataForm != null)
{
this.DataForm.CancelEdit();
}
}
}
If by any means I change the constructor, commands stop working (therefore I cannot put my Interface into the constructor).
What I need to do, is under CommitEdit, except for updating the usercontrol, I also want to do a separate call, which would save particular user's changes under the database (my IEmployeeRepository takes care of all).
That's why I need to find a way, how to achieve it this 'proper' way. I can surely re-style the template of this control and rebind OK & Cancel buttons, but I don't believe that's the way to go.
FINAL
ServiceLocator did the job. Here is the code:
_employeeRepository = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IEmployeeRepository>();
There's ServiceLocator.Current.GetInstance which can provide you with any dependency everywhere.
But be careful, as the dependency is pretty much hidden.

prism ConfigureContainer what for

I m reading Prism library by microsoft. I just dont understand ConfigureContainer().
The code in shell application like this:
internal class JamSoftBootstrapper : UnityBootstrapper
{
protected override IModuleEnumerator GetModuleEnumerator()
{
return new DirectoryLookupModuleEnumerator("Modules");
}
protected override void ConfigureContainer()
{
Container.RegisterType<IShellView, Shell>();
base.ConfigureContainer();
}
protected override DependencyObject CreateShell()
{
ShellPresenter presenter = Container.Resolve<ShellPresenter>();
IShellView view = presenter.View;
view.ShowView();
return view as DependencyObject;
}
}
public interface IShellView
{
void ShowView();
}
public class ShellPresenter
{
public IShellView View { get; private set; }
public ShellPresenter(IShellView view)
{
View = view;
}
}
I want to understand why I register IShellView,Shell in ConfigureContainer(). Whats going on behind sceens?
I've registered to give you answer :). ConfigureContainer is just method from abstract class UnityBootstrapper where you can register all Prism services (ModuleManager, RegionManager, EventAggregator). You can make your custom Bootstrapper class, where you can manage your dependency. In your case, every time when you in your code ask for IShellView, you'll get instance of Shell.
I recommend you this book. It's free.
UnityBootstrapper provided by PRISM internally uses UnityContainer to resolve dependency for your registered objects. You can configure your custom objects with it like you are doing in sample.
In your sample, you registered Shell with instance IShellView.
Container.RegisterType<IShellView, Shell>();
So whenever you ask container to get you object for IShellView, it will give you an instance of Shell object.
IShellView shellObject = Container.Resolve<IShellView>();
So, ConfigureContainer gives you support to register your objects with it's container.
Quote from MSDN link:
Configures the IUnityContainer. May be overwritten in a derived class
to add specific type mappings required by the application.
Also, you can read more about it here - Managing Dependencies Between Components Using the Prism Library 5.0 for WPF.

Circular dependency when adding reference (Delegates)

Basically I have 2 projects, a form and a user control.
I need both of them to be in different projects but the form need to refer to the user control as it is using the user control. And the user control will need to refer to the form as it is using one of the form class. When I add the second one because it need the , VS will complain circular dependency which is understandable. How do I solve this?
Logically the form should depend on the user control. You could create an interface to replace the form within the user control project, and then have the form implement that interface.
Example user control project;
public interface IForm
{
string MyString { get; }
}
public class MyUserControl : UserControl
{
public IForm Form { get; set; }
private void ShowMyString()
{
String myString = Form.MyString;
...
}
}
Example Form project
public class MyForm : Form, IForm
{
public MYString { get "My String Value"; }
}
I think the root cause of your problem is that you haven't separated your concerns between the form and the control properly.
Since you have a (somewhat generic) control, it shouldn't depend on the form. All of the logic of the control should reside within the control itself. The form should only black-box consume the control: add it, set public fields, call public methods, etc. anything else is a violation of encapsulation.
Sometimes, controls may need to know things about their parent form. In this case, I would suggest something as simple as adding a Parent field to the child control.
if you need something more specific from the form, you can always add an interface; the interface should only list those things that the control needs from the form. For example, if you need the size, you can add:
public interface IControlParent {
int Width { get; }
int Height { get; }
}
This way, you clearly see the dependencies (what the control needs from the parent), and if the parent type/contract changes, you don't need to do as much to change your control class.
You must sepárate your code, its never a good idea to have a reference to an application assembly, if you try to reuse it in the future, the applications exe should go with the control.
So, take the class from the form project and move it to the control project or create a library project, put the class on it and reference it from your control and your app projects.
You should use an event (delegate). Let's assume that inside your form project you created one class: Form1. And inside user control you defined UserControl1.
UserControl1 needs to instantiate and call a method from Form1:
public class Form1
{
public void Execute(string sMessage)
{
Console.WriteLine(sMessage);
Console.ReadLine();
}
}
UserControl1:
public class UserControl
{
public Func<object, object> oDel = null;
public void Execute()
{
oDel?.Invoke("HELLO WORLD!");
}
}
And from the class that instantiate UserControl, let's call it ParentClass:
public class ParentClass
{
public void Execute()
{
UserControl oUserControl = new UserControl();
oUserControl.oDel = Form1Action;
oUserControl.Execute();
}
public object Form1Action(object obj)
{
string sObj = Convert.ToString(obj);
Form1 oForm = new Form1();
oForm.Execute(sObj);
return null;
}
}
This approach gives the responsibility of handling an event to the high level class.

Ninject Form Clarification

I have a ModuleLoader : NinjectModule which is where I bind everything.
Firstly I use
Bind<Form>().To<Main>();
to Bind a System.Windows.Forms.Form to my Main form.
Is this correct?
Secondly in the Program.cs I use this:
_mainKernel = new StandardKernel(new ModuleLoader());
var form = _mainKernel.Get<Main>();
Where _mainKernel is a ninject standard kernel.
Then I use Application.Run(form)
Is this correct?
I'm unsure as to what to bind together when it comes to Windows.Forms.
Thanks for any help.
You shouldn't really be binding to System.Windows.Forms.Form. Ninject is primarily meant for binding interfaces to concrete types so that you can pass around dependencies as interfaces and switch out the concrete implementation at runtime/during tests.
If you just want to use Ninject to create your Form in this way though, you'd simply use Bind<MyForm>().ToSelf() then do kernel.Get<MyForm>(). If you are requesting the concrete type directly though and it doesn't take any dependencies, there's not much point in using Ninject to initialise it.
In your situation, if your form implements an interface then you would do: Bind<IMainForm>().To<MainForm>() and request the interface type from Ninject. Usually your interface shouldn't be bound to the concept of a "form" though, it should be agnostic of the implementation (so later you could produce a CLI and website version and simply swap the Ninject bindings).
You could use the Model-View-Presenter design pattern (or a variant) to achieve this like:
public interface IUserView
{
string FirstName { get; }
string LastName { get; }
}
public class UserForm : IUserView, Form
{
//initialise all your Form controls here
public string FirstName
{
get { return this.txtFirstName.Text; }
}
public string LastName
{
get { return this.txtLastName.Text; }
}
}
public class UserController
{
private readonly IUserView view;
public UserController(IUserView view)
{
this.view = view;
}
public void DoSomething()
{
Console.WriteLine("{0} {1}", view.FirstName, view.LastName);
}
}
Bind<IUserView>().To<UserForm>();
Bind<UserController>().ToSelf();
//will inject a UserForm automatically, in the MVP pattern the view would inject itself though
UserController uc = kernel.Get<UserController>();
uc.DoSomething();

Is this a legitimate use of a Singleton?

(Disclaimer: This question is not specific to ASP.NET)
I have a control which may be templated, similar to the login controls:
public abstract class TemplatedControl : CompositeControl
{
public ITemplate Template { get; set; }
protected override void CreateChildControls()
{
var template = this.Template ?? CreateDefaultTemplate();
// ...
}
protected virtual ITemplate CreateDefaultTemplate()
{
return null;
}
}
A templated control would look like:
public class FooControl : TemplatedControl
{
public override ITemplate CreateDefaultTemplate()
{
return new FooTemplate();
}
}
My question is: would a Singleton be appropriate here instead?
public override ITemplate CreateDefaultTemplate()
{
return FooTemplate.Instance;
}
Singletons are associated with global variables; in this case, there is no state.
Singletons are also associated with hard-coded dependencies. In this case, knowledge of the specific type is warranted.
In this case I would say not. In the pattern you are proposing, there would only ever be one FooTemplate, which would be shared across multiple controls, pages and threads. You would have to be very careful that the template did not contain any request or user specific information, and also synchronize any method calls. It is much easier, and just a bit less performant to instantiate it each time.
The only reason I see doing it that was is that it takes a long time to instantiate the control. In that case, I would go with a factory pattern, where any initialization is done once, but all the data copied into a new instance every time.
If you only want the template created once for the control, you could use lazy initialization instead and achieve nearly the same effect.
private ITemplate defaultTemplate;
public override ITemplate CreateDefaultTemplate()
{
if (defaultTemplate == null)
{
defaultTemplate = new FooTemplate();
}
return defaultTemplate;
}
You should only use a Singleton implementation if you are sure that you only want one instance of any particular object ever in your application.
Do you really want to use the exact same instance for the template control? We may need some more info about what you are trying to accomplish. How many places does TemplatedControl get used in the same application?

Categories