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);
Related
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>.
I am trying to make a windows form to be reused across several projects. So I thought this would be a class library. What I want to do is call WindowsForm.Show() as a static call. I've tried to create a class library but I cannot reach the resources for an icon. I then created a WinForms project, but it wants a 'New' in the program.cs. I do not want to call
WindowsForm form = new WindowsForm()
form.show()
How do i create a reusable static form? I hope i'm being clear.
To add a factory method you can do:
public static MyForm ShowNew()
{
MyForm form = new MyForm();
return form;
}
About resources, it depends on what you're using the icon for. If the icon is always the same and it always will be, then add it to your library Properties/Resources (if you don't see it, go on your library project properties, on the tab Resources, and click add). Otherwise, add a parameter to your factory method (and to the form constructor as well) and pass it when you call the form:
public static MyForm ShowNew(Icon ico)
{
MyForm form = new MyForm(ico);
return form;
}
When instantiating
MyForm.ShowNew(Properties.Resources.my_icon);
Another approach would be implementing your form as usual, wrapping it in a Singleton-like class :
public static class WindowsFormSingleton {
// A static instance of your form
private static WindowsForm _form;
// A singleton property to interact with the form.
public static WindowsForm Instance
{
get
{
if(_form == null) {
this._form = new WindowsForm();
}
else if(_form.IsDisposed) {
this._form = new WindowsForm();
}
return this._form;
}
}
}
... and using the singleton instance like the following :
WindowsFormSingleton.Instance.Show();
Note: As taffer stated in the comments, using a Factory method to always create a new instance would be a better approach to this.
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?
I have a textbox in my main form.
Now I have created a function which is used to set the value of the text box.
public void SetTextOfTextBox(String text)
{
textbox1.text = text;
}
Now in my main form I call another class (class b) which does some work for me. Now i want to be able to call my setTextofTextBox function from class b.
Now if I try Form1.SetTextOfTextBox("test"); this doesn't work.
What am I doing wrong?
How do I access components of a a Form from another class.
Form1.SetTextOfTextBox("test"); this doesn't work
This doesn't work because SetTextOfTextBox is not static and you cannot access a non-static function directly. And you can't make it static either because your textbox is form level control.
How do I access components of a a Form from another class
You will have to pass the instance of Form1 to your other class to access it. Something like
Class B = new ClassB(this); //where this is the instance of Form1.
You will need a reference to the instance of Form1 in class b, otherwise you cannot call member methods.
Something like this:
class Form1 : System.Windows.Forms.Form {
void functionInForm1() {
ClassB objB = new ClassB();
objB.doSomething(this);
}
}
class ClassB {
void doSomething(Form1 form) {
form.SetTextOfTextBox("test");
}
}
Find out the Form1 and call the method:
foreach (var form in Application.OpenForms) {
Form1 myForm = form as Form1;
if (!Object.ReferenceEquals(null, myForm)) {
myForm.SetTextOfTextBox("Test");
break;
}
}
Did u try using delegates.
Specify the delegates in your ClassB like this.
public delegate void OnDone(string textValue);
public event OnDone OnUserDone;
after completing the task in ClassB call event:
OnUserDone("DoneClassB");
When u create the object of class in form map delegate function.
Classb b=new Classb();
b.OnUserDone += new Classb.OnUsrControlDone(CompletedClasss);
Define the function in form like below.
void CompletedClasss(string textValue)
{
SetTextOfTextBox( textValue);
}
I have a windows form and my own class in my project
I have a method in my own class
public object Sample(Form MyForm,string ComponentName)
{
}
I want to get components of the "MyForm" from another class How Can I Make THIs?
form class
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
MyOwnClass
public class Sample
{
public object GetComponentMethod(Form form,string ComponentName)
{
////
}
}
Have you tried with:
Control myControl= form.controls.Find(...)?
updated
Sorry but in this case I cannot understand what are you looking for!
updated
you have to create a public property Components! So you can retrieve data you need!
It looks like you are just trying to access members of one object from another object.
If so you need to expose some way of accessing a specific instance of a class.
If you will only ever have one instance (of say your Form1) the simplest way is to expose that single instance via a public static property. This is called a singleton pattern:
public partial class Form1 : Form
{
public static Form1 Singleton { get; private set; }
public Form1()
{
Form1.Singleton = this;
InitializeComponent();
}
}
You can the access your Form1 instance using Form1.Singleton.SomeProperty from anywhere.
I am not promoting any specific Singleton pattern here, as there are too many issues over thread safety, but for your simple example this will do the job. Call the static property "Singleton" or "This" or "SolutionToMyWoes" or whatever you like!