How to access form instance from form controls - c#

I am using C# and a very old version of .Net. I have hundreds of forms and a class. Each form has a ProcessDump() method. When a form is opened, its controls are passed to my class. I need to call ProcessDump method and all i have is controls of the form. How can I access to the method?

Have each form implement an interface called IProcessDump:
interface IProcessDump
{
void ProcessDump();
}
Assuming WinForms, there is the FindForm method going back to .NET 1.1. Grab the form and test it for the interface:
Form formRef = myControl.FindForm();
IProcessDump procDump = formRef as IProcessDump;
if (procDump != null)
{
procDump.ProcessDump();
}
Alternatively, controls have a Parent property. For controls placed directly on the form the parent will be the form:
foreach (Control c in myControls)
{
if (c.Parent != null && c.Parent is Form)
{
// Found, go nuts.
}
}
The null check might not be required.

Related

Calling object from mainform.cs in other forms?

i want to call anything from mainform (mainform.cs) from GraficDisplay (namespace)
in the other (namespace) : GraphLib , but i can't , would any one tell me why? and how can i resolve this problem? Its been giving me hard time since the start of the project, every time I try these errors appear:
When I call:
mainform.toolstriplabel1.text = "87";
this appears:
The name 'mainform' does not exist in the current context
and if I call this:
GraficDisplay.MainForm.toolstriplabel1.text = "87";
this appears:
The name 'GraficDisplay' does not exist in the current context
I mean I even can't call the GraficDisplay (namespace) in GraphLib (namespace)
also the MainForm is public and partial.
I usually don't follow links here either but CodeProject is a rather reliable source imo, so I had a look.
Edit: I was confused as to what you want. Here is what you seem to actually wnat:
The problem is about referencing a form or part of it from another form or part of it. It is further a problem of dealing with a Library, that really shouldn't be messed up be adding namespaces of an example application or dependencies etc..
So what you want is loose coupling.
Here is a solution that uses references in the library objects and register methods to fill the references. If you don't register anything the library will work normally.
This solution can be changed and expanded but I'll leave it at registering two objects: One is a Control, eg.g a TextBox; the other is a Component e.g. a ToolStripItem. If you want to reference only the ToolStripItem you can omit the references to the Control and the RegisterCtl methods.
In hat case you can and should also substitute 'Component' for 'ToolStripItem' to make things thighter!
First you go to the ultimate consumer of the actions, PlotterGraphSelectCurvesForm. Here you add these two blocks of code:
public partial class PlotterGraphSelectCurvesForm : Form
{
private int selectetGraphIndex = -1;
private PlotterGraphPaneEx gpane = null;
// block one: a Control reference (if you like!):
Control myTextCtl;
public void RegisterCtl(Control ctl) { if (ctl != null) myTextCtl = ctl; }
// block one: a Component reference:
Component myTextComp;
public void RegisterComp(Component comp) { if (comp != null) myTextComp = comp; }
//..
Next you code what you want to happen, maybe like this:
void tb_GraphName_TextChanged(object sender, EventArgs e)
{
if (selectetGraphIndex >= 0 && selectetGraphIndex < gpane.Sources.Count)
{
DataSource src = gpane.Sources[selectetGraphIndex];
String Text = tb_GraphName.Text;
// all controls have a Text:
if (myTextCtl != null) myTextCtl.Text = Text;
// here you need to know the type:
if (myTextComp != null) ((ToolStripItem) myTextComp).Text = Text;
//..
}
In theory all you now need to do is to register the TextBox and/or the ToolStripItem in the Mainform.. However, there is a complication: The PlotterGraphSelectCurvesForm is not called from the Mainform! Instead it is called from a UserObject PlotterGraphPaneEx, which in turn is sitting in the MainForm. In the same sprit of not messing up the library by creating dependencies you simply add the very same references and registration methods to this UO as well:
public partial class PlotterDisplayEx : UserControl
{
#region MEMBERS
Control myTextCtl;
public void RegisterCtl(Control ctl) { if (ctl != null) myTextCtl = ctl; }
Component myTextComp;
public void RegisterComp(Component comp) { if (comp != null) myTextComp = comp; }
//..
Now you can actually register things, both in the MainForm..:
public MainForm()
{
InitializeComponent();
display.RegisterCtl(aDemoTextBox);
display.RegisterComp(toolstriplabel1);
//..
..and in the UO:
private void selectGraphsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (GraphPropertiesForm == null)
{
GraphPropertiesForm = new PlotterGraphSelectCurvesForm();
GraphPropertiesForm.RegisterCtl(myTextCtl);
GraphPropertiesForm.RegisterComp(myTextComp);
}
//..
Now when you open the Properties form and change the LabelText you can see both the text in the Graphs and the text in both the Menu and the TextBox change..

Create a new instance of a control dynamically

Ok so I have a form I'm building that's going to change it's interface by using custom controls. What I'm trying to do is make several checks before a new control is created, like checking if one is already up. I have everything working just fine but I can't seem to create the new control dynamically, without creating it before running the checks which defeats the purpose.
The controls all implement an interface called ICustomControl and inherit from a MasterControl. Then I have a custom control called JobForm, and a button on the main form that calls the method like so: Check_Controls(newJobForm)
JobForm newJobForm;
private void Check_Controls(Control control) // Checks current controls to see if a new one can be opened
{
bool currentControl = false;
foreach (Control c in this.Controls)
{
if (c is ICustomControl && c != masterControl)
currentControl = true;
}
if (currentControl)
{
TimedMessageBox timedMessage = new TimedMessageBox("There is currently an open form. Please close the current control before opening another.");
timedMessage.ShowDialog();
}
else
{
Control c = (Control)Activator.CreateInstance(control.GetType());
this.Controls.Add(c);
Position_Control(c);
c.Show();
}
}
I dont't want to create a new instance of the custom control like: JobForm newJobForm = new JobForm(); before running the check method, I want to pass the reference to the check method and then have it create the new instance after it's checks are complete. In this way no matter how many new custom controls I wind up adding to the application, all I have to do to set one up is create the reference variable, then for the button call the check method and pass it the reference.
Anyone know how I can do this?
I think you are thinking about the problem backwards. Instead of saying "I have control X, is it valid?" think "would control X be valid, if so create". You are wanting to make checks to see if a control is valid, yet you want to send in a reference to that control.
Your code doesn't check for specific types of control, but rather just that at least one control belonging to the current form implements your interface. If this is the intended behavior, just have a function that does your initial check to see if any ICustomControl exist on your form. If that function return false, then go along with your creation.
You can accomplish this with a single function by using a constrained generic. This also gets you away from the less-than-ideal practice of using Activator and other reflection methods for dynamic type generation:
private void CheckAndAddControl<ControlType>()
where ControlType : MasterControl, new()
{
bool currentControl = false;
foreach (Control c in this.Controls)
{
if (c is ControlType)
{
currentControl = true;
break;
}
}
if (currentControl)
{
TimedMessageBox timedMessage = new TimedMessageBox("There is currently an open form. Please close the current control before opening another.");
timedMessage.ShowDialog();
}
else
{
var c = new ControlType();
this.Controls.Add(c);
Position_Control(c);
c.Show();
}
}
You would then use this function as follows:
CheckAndAddControl<JobForm>();

C# - implementation of method which is using generic types

I am creating a MDI form and I have a method that loads the different forms. Now I need to make a little modification - I need to add functionality that calls one child form from within another child form.
Because I need this in several different places I made a new class from who all the classes that need this functionality inherits. I want to make it work with generic types so I can pass every form class that I may need like LoadAForm(MyForm1) or LoadAForm(MyForm2) and so on.. I hope I it's clear what I want as final result.
I tried this:
protected void LoadAForm<T>(ref T sender)
{
MainForm frm = this.MdiParent as MainForm;
T temp;
if (frm != null)
{
sender = SingletonFormProvider.GetInstance<temp>(frm, true);
sender.MdiParent = frm;
sender.Dock = DockStyle.Fill;
sender.Show();
}
}
which doesn't work. But I have almost no experience with generics even more when they are used in methods, so I don't know how to go on.
What I get as an error using this syntax is The type or namespace "temp" could not be found...". I'm not even sure that this is the way to do it.GetInstance<>` has to take an argument of the same type as the type of the form I'm calling.
You need to use the type parameter, not the variable name:
sender = SingletonFormProvider.GetInstance<T>(frm, true);
Also, to ensure that T is valid (as your comment suggests) you will need to constrain it:
protected void LoadAForm<T>(ref T sender) where T : Form
I don't think you need generics here. I think you could easier work by simply using Form as the concrete type:
protected void LoadAForm(ref Form sender)
{
MainForm frm = this.MdiParent as MainForm;
Form temp;
if (frm != null)
{
sender = SingletonFormProvider.GetInstance(frm, true);
sender.MdiParent = frm;
sender.Dock = DockStyle.Fill;
sender.Show();
}
}

C# Closing a specific form in a multiform application

I hope you can help me with this one. My application is monitoring a database for alerts. When a alert shows up in the database, my application will add it to the main form in a datagridview, and depending on its priority it will also create a small winform popup with the event.
In the datagridview is a button to flag the alert as "seen", it will then update the database and be it will be gone from the list. However the popup form for this event is still open.
Does anyone know how to close this form? I need a way to find a specific form between the possible multiple alert forms that are open.
The closest I have come this far is the following code:
private void closeForm(int id)
{
foreach (Form f in Application.OpenForms)
{
if (Convert.ToString(id) == f.Name)
{
this.Close();
}
}
}
Which works up until the point that it closes the correct form. then it gives an error saying "Collection was modified; enumeration operation may not execute." This kinda makes sense, but I simply can't figure out another way to do it.
I have a winform class called Alert, wich makes the new forms. As you can see they will get a standard text "Alarm" and a unique Name based on the alert ID.
Alert alertform = new Alert(id);
alertform.Name = formid;
alertform.Text = "Alarm";
alertform.Show();
I hope someone has some good ideas how I can go about this. I have searched but cannot realy find a simple and elegant way to do this.
You need to add break; to your loop after you close the form. The collection is modified when you close the form (that form is removed from the collection), thus rendering the foreach loop invalid. And should you not be calling f.Close, rather than this.Close?
private void closeForm(int id)
{
foreach (Form f in Application.OpenForms)
if (Convert.ToString(id) == f.Name)
{
f.Close();
break;
}
}
You should simply be able to store a reference to your form in the DataGridView or its DataSource then close the form using that reference. This approach is less likely to break in the future than iterating over all the application forms.
What would probably work best is to add a hidden column to the DataGridView that holds the form id, then also have a Dictionary<int, Form> which you use to get the reference to the Form you want to close.
Then you can simply get the form reference out of the dictionary and call close on it:
private void CloseAlertForm(int id)
{
Form f = dict[id];
f.Close();
dict.Remove(id);
}
Additionally you could store Action delegates instead of form references allowing you to slightly decouple the alert forms and the grid form.
just get ref. from foreach loop and close the form outside it.
private void closeForm(int id)
{
Form formtoclose=null;
foreach (Form f in Application.OpenForms)
{
if (Convert.ToString(id) == f.Name)
{
formtoclose = f;
}
}
if(formtoclose!=null)
formtoclose.Close();
}
A Close modifies your OpenForms collection, so instead of enumeration over the OpenForms collection directly, you could enumerate over a copy.
LINQ is very handy into making copies, like this:
foreach (Form f in Application.OpenForms.Where(i => Convert.ToString(id) == i.Name).ToList()) {
// Save to close the form here
}
The ToList executes the query, and stores the copy.
var names = Application.OpenForms.Select(rs=>rs.name).ToList()
foreach (string name in names)
if (Convert.ToString(id) == name)
{
Application.OpenForms[name].Close();
}
You could use the type of the forms to find them (And ToArray to create a new collection an avoid changing the collection you are enumerating).
private void CloseAlerts()
{
var openForms = Application.OpenForms.Cast<Form>();
foreach (var f in openForms.Where(f => f is Alert).ToArray())
{
f.Close();
}
}
In this case you don't need to set a name :
Alert alertform = new Alert(id);
alertform.Text = "Alarm";
alertform.Show();

Assigning methods to events on user controls declared in an interface

In C#, I am building custom controls to be used in a custom application. I want each control to implement an event that will fire if an exception or error (an internal check failure) occurs inside the controls. I created an interface that declares the event. I create user controls that implement the interface. Here's my problem.
When I add one of my custom controls to a form, I want to loop through the controls on the form, detect all controls that are my custom controls and then assign an event handler to the event I declare in the interface. I cannot find a way to cast an object to the type of the interface.
Consider:
interface IMyInterface
{
event ControlExceptionOccured ControlExceptionOccuredEvent;
...
}
public partial class TextControl : UserControl, IMyInterface {
...
public event ControlExceptionOccured ControlExceptionOccuredEvent;
...
}
and on my form I use one of these TextControls. I have this method:
private void Form1_Load(object sender, EventArgs e)
{
foreach (Control Control in Controls)
{
if (Control.GetType().GetInterface(typeof(IMyInterface).FullName) != null)
{
((IMyInterface)Control).ControlExceptionOccuredEvent += ControlExceptionHandler;
}
}
}
This complies but will not execute. How can I add ControlExceptionHandler to the event chain?
My thanks to anyone who tries to help.
As much as I understand yuo're not able to subscribe to the event cause IF condition returns FALSE. Did yuo try to write something like this ? :
foreach(Control ctrl in this.Controls){
if((ctrl as IMyInterface) != null) {
//do stuff
}
}
This is a simpler way to do it:
if (control is IMyInterface)
((IMyInterface)control).ControlExceptionOccuredEvent += ControlExceptionHandler;
... But the way you're doing it should work too, so you'll have to provide more details about what's happening.
The code
((IMyInterface)Control).ControlExceptionOccuredEvent += ControlExceptionHandler;
generates
Unable to cast object of type '...Text.TextControl' to type 'IMyInterface'.
I do not understand why not.
As a side note, I replaced
if (Control.GetType().GetInterface(typeof(IMyInterface).FullName) != null)
with
if (Control is IMyInterface)
and it does not work. The second example never returns true. I also tried
if ((Control as IMyInterface) != null)
and it also never returns true.

Categories

Resources