Dependency Injection with Windows Forms - c#

I have a very simple C# Windows Forms application that I have decided to convert to use Dependency Injection via Ninject.
The entry point for my application looks like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// initialize dependency injection
var kernel = new NinjectKernel();
kernel.ApplyBindings();
var form = kernel.GetForm();
Application.Run(form);
}
And my Form looks like this:
public partial class MainForm : Form
{
private readonly ISomething Something;
public MainForm()
{
Something = new Something(this);
InitializeComponent();
CreateControl();
Something.Init();
}
}
My problem is as follows: I want to call "Something" to do basic initialization work for my Form (e.g. Load grid data, set label text...etc)
However, I only have access to the Form components in my MainForm class. I basically need to inject my MainForm instance into "Something" so that it can apply the modifications.
What I have tried is as follows:
public class Something: ISomething
{
private MainForm Form { get; set; }
public Something(MainForm form)
{
Form = form;
}
public void Init()
{
Form.Label1.Text = "Change some text!";
}
}
This appears to work, but something tells me that this isn't using DI properly. Can somebody shed more light on the proper approach using DI with Windows Forms?

Related

set child winform properties from a parent form when using simple injector c#

I have a form1 (not mdi) which displays dialog on button click event, dialog basically is a pop up form which shows data on datagridview control.
I am using simple injector.
PopUpForm has a property called LocationData which is a datatable. I need to set that property in form1 (parent) so that data can be displayed on the PopUpForm when it is displayed on the screen.
Sorry, i am new to simple injector and still learning, any help or guidence would be appreciated. I even don't know if i am doing in a right way.
form1
On button click event
this._formOpener.ShowModalForm<PopUpForm>();
PopUpForm
public partial class PopUpForm : Form
{
public DataTable LocationData { get; set; }
public PopUpForm()
{
InitializeComponent();
}
private void PopUpForm_Load(object sender, EventArgs e)
{
dgvNearestLocations.DataSource = LocationData;
}
}
program class
static class Program
{
private static Container container;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Bootstrap();
Application.Run(container.GetInstance<Form1>());
}
private static void Bootstrap()
{
// Create the container as usual.
container = new Container();
// Register your types, for instance:
container.RegisterSingleton<IFormOpener, FormOpener>();
container.Register<Form1>(Lifestyle.Singleton); ;
container.Register<PopUpForm>(Lifestyle.Singleton); ;
// Optionally verify the container.
container.Verify();
}
}
FormOpener
public class FormOpener : IFormOpener
{
private readonly Container container;
private readonly Dictionary<Type, Form> openedForms;
public FormOpener(Container container)
{
this.container = container;
this.openedForms = new Dictionary<Type, Form>();
}
public DialogResult ShowModalForm<TForm>() where TForm : Form
{
using (var form = this.GetForm<TForm>())
{
return form.ShowDialog();
}
}
private Form GetForm<TForm>() where TForm : Form
{
return this.container.GetInstance<TForm>();
}
}
First of all, you copied the FormOpener probably from this answer. But you missed the part about Forms needing to be transient. Don't register your forms as Singleton. Especially because you dispose them, this will work one and exactly one time. The next time you would want to show a Form you will get an ObjectDisposedException.
When you register the Forms as Transient Simple Injector will tell you that the forms implement IDisposable and this is (of course) correct. But because you take care of disposing in the FormOpener you can safely suppress this warning. Register your forms like this:
private static void RegisterWindowsForms(
this Container container, IEnumerable<Assembly> assemblies)
{
var formTypes =
from assembly in assemblies
from type in assembly.GetTypes()
where type.IsSubclassOf(typeof(Form))
where !type.IsAbstract
select type;
foreach (var type in formTypes)
{
var registration = Lifestyle.Transient.CreateRegistration(type, container);
registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent,
"Forms are disposed by application code. Letting Simple Injector do this " +
"is problematic because that would need a scope, which is impossible to do.");
container.AddRegistration(type, registration);
}
}
To come to your question:
What you need is some extra infrastructure to initialize the Form.
By letting your forms implement an interface IFormInit<T> you can pass data to the form and directly show it.
public interface IFormInit<T> : IDisposable
{
DialogResult InitAndShowForm(T data);
}
To let Simple Injector create the forms based on this interface we need to register them in the container. We can let Simple Injector search for all closed implementations by supplying a list of assemblies, like this:
container.Register(typeof(IFormInit<>), assemblies, Lifestyle.Transient);
Notice that Simple Injector will automatically merge these registrations with the ones from RegisterWindowsForms. So you can now get an instance of Form by calling:
container.GetInstance<PopupForm>();
or
container.GetInstance<IFormInit<SomeDataClass>>();
You can now add this code to your FormOpener class:
public DialogResult ShowModalForm<TData>(TData data)
{
Type formType = typeof(IFormInit<>).MakeGenericType(typeof(TData));
dynamic initForm = this.container.GetInstance(formType);
DialogResult result = (DialogResult) initForm.InitAndShowForm(data);
initForm.Dispose();
return result;
}
This will get the Form from the container based on the IFormInit<T> type that it implements. When you get the form, you call the function on the interface instead of directly call Form.ShowDialog(). When the form is closed you dispose of the Form.
Note: The use of dynamic typing maybe needs clarification. Why it is needed is inspired by the QueryHandler pattern described here.
Usage is as follows:
// Add a specific class to pass to the form
public class LocationDataWrapper
{
public DataTable LocationData { get; set; }
}
public partial class PopUpForm : Form, IFormInit<LocationDataWrapper>
{
public PopUpForm() => InitializeComponent();
// Implement the interface, the loaded event can be removed
public DialogResult InitAndShowForm(LocationDataWrapper data)
{
dgvNearestLocations.DataSource = data.LocationData;
return this.ShowDialog();
}
}
On button click event
DialogResult result = this._formOpener.ShowModalForm(new LocationDataWrapper
{
LocationData = locationDataTable,
});
You can create wrapper or data classes for each form and it will automatically show the correct form, when you let this Form implement IFormInit<ThisSpecificDataClass>.

List going back to 0 after new form

I have this class:
public class Observador : iObservador
{
private List<Form> Forms = new List<Form>();
public void DeSubscribirse(Form form)
{
Forms.Remove(form);
}
public void Limpiar()
{
Forms.Clear();
}
public void Subscribirse(Form form)
{
Forms.Add(form);
}
public List<Form> DevolverSubscriptos()
{
return this.Forms;
}
}
Which is used on a base form I have like this:
public partial class FormBase : Form
{
public EE.Observador Watcher = new Observador();
public FormBase()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Hide();
}
}
Which I use for the rest of my forms to be inherited from.
My idea is to have in all the forms a reference to the object Watcher from every place, with it having a reference for every form which is subscribed to it. So I can do for example, from FormB know that FormA is already subscribed with the method DevolverSubscriptos() (this means return subscribers) and access it to make it visible again after closing FormB.
The problem is that when I start FormB the list of Watcher with the whole forms is set back to 0.
What am I doing wrong? How can I solve it?
public partial class AdminUIGI : FormBase.FormBase
That's how I reference it.
Short answer: you're using an instance field. Each form is a separate instance, hence each has it's own copy of the EE.Observador.
So a quick and dirty fix would be to make this field static, i.e. shared by all instances of the given class. And if you want to improve, you might then consider reading about the Singleton pattern (mainly because you'll see it used a lot - but read on :) ), then read why using Singleton as a global variable is in fact an anti-pattern and move on to reading about dependency injection and IoC - which is how (in vacuum at least) your code should probably end up. (Note: for a quick and dirty solution static field is all you need).
#decPL I made it work with the singleton pattern doing this ` public sealed class Singleton
{
Singleton()
{
}
private static readonly object padlock = new object();
private static Singleton instance = null;
public static EE.Observador watcher = new Observador();
private Usuario userInstance = null;`

Monitoring multiple forms from class library

I am trying to write a class NvApplicationContext which inherits from ApplicationContext, in a class library, and which I will use to monitor activity in multiple forms.
public class NvApplicationContext : ApplicationContext {
// ...
public NvApplicationContext(Form f) {
}
// ...
}
However, I can only seem to pass in one form (an instance of Form1) from my end projects:
NvApplicationContext nvca = new NvApplicationContext(new Form1());
Application.Run(nvca);
Are the rest of the forms supposed to be recognized automatically? Or, how can multiple forms be monitored by NvApplicationContext?
Your NvApplicationContext needs to be told about any form instance that you want the context class to manage; it does not happen automatically.
I can think of multiple ways to structure this:
Allow your constructor to take multiple Form instances, using params
Add a RegisterForm method to NvApplicationContext and call whenever you create a form that needs to be monitored
Add a CreateForm method to NvApplicationContext and use it to create a form that needs to be monitored
In the class library, create a base form class that registers itself on creation, and have your Form1 and Form2 inherit from that base class
Allow constructor to take multiple Forms
In the class library:
public class NvApplicationContext : ApplicationContext {
public NvApplicationContext(params Form[] forms) {
// register event handler on each form here
}
}
and used like this in the end projects:
var nvca = new NvApplicationContext(new Form1(), new Form2(), new Form3());
Application.Run(nvca);
Using a RegisterForm method
Define a static instance of NvApplicationContext:
public class NvApplicationContext : ApplicationContext {
public static readonly NvApplicationContext Current = new NvApplicationContext();
public void RegisterForm(Form frm) {
// register event handler on form here
}
}
and call RegisterForm once you've created the form. This could be before the call to Application.Run:
NvApplicationContext.Current.RegisterForm(new Form1());
NvApplicationContext.Current.RegisterForm(new Form2());
NvApplicationContext.Current.RegisterForm(new Form3());
Application.Run(NvApplicationContext.Current);
or anywhere in the application, because NvApplicationContext.Current is a static field and globally available.
Using a CreateForm method
Another option is to create any form that needs to be managed, via NvApplicationContext, and it will take care of registering the form. This assumes a static instance of the context, as in the previous section:
public class NvApplicationContext : ApplicationContext {
public static readonly NvApplicationContext Current = new NvApplicationContext();
public T CreateForm<T>() where T : Form, new() {
var ret = new T();
// register event handlers here
return ret;
}
}
and then call before Application.Run
NvApplicationContext.Current.CreateForm<Form1>();
NvApplicationContext.Current.CreateForm<Form2>();
NvApplicationContext.Current.CreateForm<Form3>();
Application.Run(NvApplicationContext.Current);
Using a base form
You could write a base form class like this (either in your class library, or in the end project), calling RegisterForm with a static instance from before:
public class BaseForm : Form {
public BaseForm() {
NvApplicationContext.Current.RegisterForm(this);
}
}
and create forms that inherit from the base class either with the UI:
Or, you could do this from the code-behind of the form. Modify the base class from Form to BaseForm:
public partial class Form1 : BaseForm {
public Form1() {
InitializeComponent();
}
}
Once you've set up the inheritance, whenever you create a new instance of the form it will automatically be registered with NvApplicationContext.Current.
new Form1();
new Form2();
new Form3();
Application.Run(NvApplicationContext.Current);

How to resolve all forms using unity container

I use unity container in my WinForms application and register interfaces and classes. but when open other forms it's not working for fetching data. It's just working for form1.
How to resolve all forms?
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
RegisterType(new UnityContainer());
}
public static void RegisterType(IUnityContainer container)
{
container.RegisterType<IBlogRepository, BlogRepository>();
container.RegisterType<IPostRepository, PostRepository>();
Application.Run(container.Resolve<Form1>());
}
}
This is constructor injection in form1():
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
private readonly IBlogRepository _blogRepository;
public Form1(IBlogRepository blogRepository) : this()
{
_blogRepository = blogRepository;
}
}
and this is constructor injection in formAddUpdate():
public partial class FormAddUpdate : Form
{
public FormAddUpdate()
{
InitializeComponent();
}
private readonly IBlogRepository _blogRepository;
private readonly IPostRepository _postRepository;
public FormAddUpdate(IBlogRepository blogRepository, IPostRepository postRepository) : this()
{
_blogRepository = blogRepository;
_postRepository = postRepository;
}
}
now when the run application I can retrieve data from from1 but when switch to add/update form, it returns error: {"Object reference not set to an instance of an object."}
How to resolve all forms in my application?
If you look at your code, you resolve exactly one type (Form1) from your container. After that, poor container dies.
RegisterType(new UnityContainer());
}
public static void RegisterType(IUnityContainer container)
{
container.RegisterType<IBlogRepository, BlogRepository>();
container.RegisterType<IPostRepository, PostRepository>();
Application.Run(container.Resolve<Form1>());
}
When you create another container somewhere else to resolve, say, Form2 it knows nothing of the registrations made with the first container, so it can resolve neither BlogRepository nor PostRepository.
So the solution is to keep the one and only container around, the one that you do all registrations with. And use that one to do all your resolving, preferentially not by passing the container around or referencing a static service locator, but instead resolve just one root object and inject factories that do all the resolving needed.

use Winform Objects inside an Static Method

I have a problem that I could ignore a long time but now I have to solve it.
I have 3 GUI (Winform) 1 Main 1 Settings and 1 for a Webbrowser.
I need to call a Method that is in my Webbrowser Form inside my Main Form for that reason the Method is a static one.
But if I set it to static it giveĀ“s me an error if I try to work with the Objects from the Form.
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
public static void WebLyrics(string url){
webBrowser1.Navigate(url);
}
}
The easiest way is to:
Add a static Instance property to the webbrowser form:
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
Instance = this;
}
public static Form3 Instance { get; private set; }
public void WebLyrics(string url)
{
webBrowser1.Navigate(url);
}
}
and call the WebLyrics method via the Instance property in the other form:
public partian class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Navigate()
{
Form3.Instance.WebLyrics("www.stackoverflow.com");
}
}
I assumed you create both forms somewhere somehow...
You should consider changing the code. Why is your Form having a static method? It doesn't make any sense. The easiest way to achieve it is to pass reference to the Webbrowser Form into the Main Form. Or you can instantiate the Webbrowser form inside you Main Form and then show it.
But I suggest that you introduce a way of separating the UI from the business logic layer. Consider to introduce MVC / MVP pattern, to have a clear separation, and then the forms do not need to have references to each other directly.
The easiest (= least code) way to solve it is probably to use singletons for the forms as other have suggested, or even simpler to use the built in forms registry:
var myForm3 = Application.OpenForms.OfType<Form3>.FirstOrDefault();
I would probably prefer to use a separate singleton for holding the forms, as I would find it more maintainable. This would be a better separation of concerns in that the forms would not have to be created as singletons and could be reused in other ways. It is truly your app's UI layout that is the singleton.
Something like this:
public class AppLayout
{
public static AppLayout Instance {
get { ... }
}
public WebBrowserForm WebBrowser {get;private set;}
public MainForm Main {get;private set;}
public SettingsForm Settings {get;private set;}
}
Think of it like a typed variant of the Application singleton.

Categories

Resources