I have these forms:
MainScreen - MDI container
DataBaseOutput - child
NewAnime - child
DataBaseOutput has a tab control that holds datagrids, each for different tables. I use an access database.
In those tabs, there is a menustrip, where the functions "New", "Edit", "Delete" etc. will be called from. Now when I'm on the first tab's menustrip and click on "New" I want to open the form "NewAnime", inside the MDI container. This however, is not working as I planned. At first I tried to just call it from the childform (DataBaseOutput). This resulted in opening a new form instead of a child. when I made it a child it was not showing up.
Then I triend lots of things, but I still haven't figured it out.
This is the current code for calling the form. It calls the form with a method in the main form:
private void NewAnime_Click(object sender, EventArgs e)
{
MainScreen main = new MainScreen();
main.mShowForm(2);
this.Close();
}
Method in the main form:
// Forms for MDI Parent
DataBaseOutput OutputForm = new DataBaseOutput();
NewAnime AddAnime = new NewAnime();
// How i made them childs (this is at the InitializeComponent(); part)
OutputForm.MdiParent = this;
AddAnime.MdiParent = this;
public void mShowForm(int formnumber)
{
switch (formnumber)
{
case 1: OutputForm.Show(); break;
case 2: AddAnime.Show(); break;
}
}
Does anyone have a clue of what i'm doing wrong and maybe has a better idea? This might be a bit too much working around, but as I said, it's my first time using MDI forms and i'm just trying to get it to work.
Have you set the MainForm to be an MDIContainer? To do this set its IsMdiContainer property to true; Also check it has File and Window top-level menu items and New and Close menu items. (The tutorial suggests this, I know it should have a Window menu item at least).
Have a look at this tutorial for more guidance: Creating MDI Child Forms (MSDN)
EDIT: Looking at it more closely, it seems you are creating a new instance of MainForm, and trying to show the form as a child of that instance, as opposed to showing it in the existing MainForm. I assume you already have an instance of MainForm open at this point? And assuming the OutputForm and AddAnime forms are children of MainForm you could call the existing instance's method like this:
private void NewAnime_Click(object sender, EventArgs e)
{
this.ParentForm.mShowForm(2);
this.Close();
}
but ideally you should have an event on DataBaseOutput that MainForm listens to, and shows the new Form when the event is raised. See here for more info (it talks about user controls and not child forms, but the principle is the same):
Calling parent form functions from a user control
Related
How can I show topmost a button clicked child form in my parent form (with a tabcontrol which docked as fill)?
It always shows the form at the back of the tabcontrol of the parent form, I've even used:
frm.TopMost = true;
frm.BringToFront();
Still shows at the back.
What you want is not possible. MDI children of a control get shown on a control (which you can't directly select) called MdiClient, which is not transparent (and can't be) and by default, goes always to the back of other controls in the parent form.
So the only way to do this, would be getting the MdiClient over the controls in the parent form: this would do what you expect, but it would also hide the parent controls when there are no child forms displayed (since again, the MdiClient is not, and can't be transparent).
So the only reasonable way would be having a maximized child form with the TabControl, instead of having that TabControl directly on the parent.
Or you could have your TabControl only shown when there are no child windows. For that, make a timer in the parent form, and check this at every interval:
if(MdiChildren.Length > 0)
myTabControl.SendToBack();
else
myTabControl.SendToFront();
This will only work if the MDI children are always maximized: your TabControl will not be visible when there are any children (no matter if they cover it or not)
Update
As remarked in the comments, you can have "your own MDI", by having a host control (a Panel, for example) in the parent form and loading the child forms in that control:
var form = new ChildForm();
form.TopLevel = false;
form.Parent = myHostPanel;
form.Show();
This would show the form inside the panel (which you can locate and zorder where you want)... you lose all the MDI management though, and you'll have to keep track of your children (and take care of the forms' events if needed) yourself.
I'd not use this solution, it's pretty hacky and can get messy for big applications (unless you do a correct system)
As a summary
Since we're discussing these methods in the comments
You can hack your way to do what you want, but you'll come into all sorts of problems with any approach.
If I was you, I'd redesign my application so that what you want to achieve is not needed. If you can't do that, the only sane way would be just not having those controls in the parent form, have an always-maximized, non-closable MDI child form with those controls, and skip that window everytime you need to work in the MDI children collection.
Please explain what components of what frameworks you are using and what you have done so far. Without that information i suggest the following solution (untested).
In the "ButtonClick" event of your ParentForm do this:
ChildForm cf = new ChildForm();
cf.MdiParent = this;
cf.Show();
If this doesn't work you may add a
cf.Focus();
This question make me uncomfortable :). after a lot of testing, I can't really find a solution. neither BringToFront() Function nor SendToBack() Work Properly. maybe the following approach can help you. I use IntersectWith Function of Rectangle Class and test if form intersect with tabControl or not. if so change the tab control visibility to false otherwise true. take a look at the following code:
first make form declaration public at the mdi parent form:
public partial class MdiParentForm : Form
{
Form frm = new Form();
}
After that when you initialize your child form, add some handler to its locationChanged event, like this:
frm.MdiParent = this;
frm.LocationChanged += Frm_LocationChanged;
frm.Show();
And at the end, This is the handler:
private void Frm_LocationChanged(object sender, EventArgs e)
{
Rectangle tabControlRectangle = new Rectangle(tabControl1.Location, tabControl1.Size);
Rectangle childFormRectangle = new Rectangle(frm.Location, frm.Size);
if (tabControlRectangle.IntersectsWith(childFormRectangle))
{
tabControl1.Visible = false;
}
else
{
tabControl1.Visible = true;
}
}
Thanks to #Jcl, Problem with this is that the tab control will hide and show as long as any point of the child form touches its rectangle. Moving the child form around would be horrible :-)
I am using DevExpress NavBar as main menu for my MDI application, and one of NavBar's groups contains items that represent opened MDI child forms. I am having trouble with updating a menu when a MDI child form closes.
I have to use Form.MdiChildren collection to generate menu group, but the problem is, when using Form.FormClosing event, that closed form is still in Form.MdiChildren collection. I tried to use a System.Timers.Timer to wait 1 second and then update a menu, but I get various exceptions because of asynchronous behavior (when a user closes few forms very fast).
I also cannot maintain my own list of MDI children, because of complexity of classes design.
Does anyone have some elegant solution for this?
I have had success with using this combination of methods:
private List<Form> _childForms = new List<Form>();
protected override void OnMdiChildActivate(EventArgs e)
{
base.OnMdiChildActivate(e);
Form form = ActiveMdiChild;
if (form == null)
return;
else
{
if (!_childForms.Contains(form))
{
_childForms.Add(form);
form.FormClosed += mdiChildForm_FormClosed;
}
}
}
private void mdiChildForm_FormClosed(Object sender, FormClosedEventArgs e)
{
var form = (Form)sender;
if (_childForms.Contains(form))
_childForms.Remove(form);
if (_childForms.Count > 0)
_childForms[_childForms.Count - 1].Activate();
}
Note that the Activate method is called pretty much anytime the user interacts with a child form. That includes opening and closing them.
You can then make use of the childForms collection to always know the open forms and do what you like with them.
"I also cannot maintain my own list of MDI children, because of complexity of classes design."
Is this because of the different class types?
What about holding a list of base classes? like: List<Form> When there is a FormClosed event, just remove that form from the list.
I want to access variables of a form from another form. On clicking a button inside my Main form, I want to set my Main form as Parent, then bring up another form (child form) wherein I will access variables of the Main form. My click handler is as follow:
private void btnSystem_Click(object sender, EventArgs e)
{
Form_EnterPassword EP = new Form_EnterPassword();
EP.Parent = this; //error: Top-level control cannot be added to a control
EP.ShowDialog();
}
It compiles fine without any error. However, when I run the Main form and click on the System button, it throws me an exception. I do something similar in another code (not mine) with the same button click, and encounter no error (just with setting Main form as Parent).
What am I doing wrong? Is there something in my Main code that cause this?
Best way would be to use EP.ShowDialog(this) and later use Owner property.
You need the EP.TopLevel property to be set to false. It will let you to set a parent to it.
Further reading.
In case you only want to access variables and controls of another form, then maybe you can reach it in other ways, not trough a Parent relationship.
OK,
apparently the way to do it is to call
Form_Child.ShowDialog(this)
and then I can call
FromParent_aVariable = ((Form_Parent)this.Owner).aVariable;
or if I define aVariable in the namespace Properties then
FromParent_aVariable = NameSpace.Properties.Settings.Default.aVariable;
there are two ways.
Form_EnterPassword EP = new Form_EnterPassword();
EP.MdiParent = this;
EP.Show();
try this way, it helps for me. you need to set principalform as isMdicontainer = true at the form properties
I had a similar situation recently.
I was attempting something similar but by controlling the Child Forms from a different class.
Note(s):
You're trying to set the Child Form(s) "TopMost" to something that does not allow it.
In this case the "MdiContainer".
To accomplish this:
• Disable MainForm "isMdiContainer" property (its use is kind of obsolete anyway).
• Set the Form(s) TopMost properties to true.
• You should now be able to accomplish your feature.
**Code Example:**
/* On your Main Form Class */
private void btnSystem_Click(object sender, EventArgs e)
{
// Instantiate the Form_EnterPassword by passing the MainForm
Form_EnterPassword EP = new Form_EnterPassword(this);
EP.Show(); // No longer as modal Form to display in front.
}
/* Under your EnterPassword Form Class */
// Do not create a new Instance of MyMainForm.
// You want to use the same thread as your MainForm
private MyMainForm mainForm;
/* Constructor */
public Form_EnterPassword(MyMainForm form)
{
mainForm = form;
this.Owner = mainForm; // "this" refers to the: EnterPassword Form.
}
Remarks:
The only additional thing that you (may) have to do, (to achieve perfection) is to check the MainForm > WindowState; and create a code block to minimize or bring the Forms to their specific state.
i.e:
if (WindowState == FormWindowState.Minimized)
{ /* Code to Minimize all the Child Forms. */ }
else { /* Code to bring all Forms to their "Normal" State */ }
Writing this way, made the dialog display on the center of the parent form.
Form_Child.StartPosition = FormStartPosition.CenterParent;
Form_Child.ShowDialog(this);
In my C# application there is a main form with a panel main_panel. Whenever the user selects something in the menu, lets say "A", the main panel switches to the form A's panel (look at the code), A_panel.
Since A_panel covers every controls in form A, I can summon all of the controls of form A into main form.
if ((string)MainMenu.SelectedItem == "A")
{
FormA A = new FormA();
new_panel = A.Controls["A_panel"] as Panel;
}
this.main_panel.Controls.Clear();
this.main_panel.Controls.Add(new_panel);
My question is when user selects menu A again, I don't want to recreate FormA again by new FormA(). I did because when I add control to the main_panel, then the A_panel's ownership changes to main form so that it was possible to add the control to main_panel again.
So, how to change the ownership of A_panel to Form A again?
And how to solve my problem generally?
Any reason you can't use a UserControl and keep a reference to it on your form? That way you can just show/hide it when the user wants to change views. It would be much easier to extend and interact with. Here's a walk-through on MSDN to get you started.
It sounds like a UserControl would suit your needs better. Have one instance on FormA and another on your MainForm.
If, for some reason, you can't use user controls, you could at least extract the code outside the autogenerated designer file (which I assume is why you're using FormA at all).
As in:
FormA
public FormA()
{
var mainPanel = CreateMainPanel();
this.Controls.Add(mainPanel)
}
public Panel CreateMainPanel()
{
//...build up your control tree. Cut the code from the designer if necesssary
}
MainForm
FormA _A = new FormA();
public void HandleMainMenuClick(object sender, EventArgs e)
{
if ((string)MainMenu.SelectedItem == "A")
{
new_panel = _A.CreateMainPanel();
}
this.main_panel.Controls.Clear();
this.main_panel.Controls.Add(new_panel);
}
I have a need to close a parent form from within child form from a Windows application. What would be the best way to do this?
I ran across this blog entry that looks like it will work and it uses the Event Handler concept from D2VIANT Answer
http://www.dotnetcurry.com/ShowArticle.aspx?ID=125
Summary:
Step 1: Create a new Windows application. Open Visual Studio 2005 or 2008. Go to File > New > Project > Choose Visual Basic or Visual C# in the ‘Project Types’ > Windows Application. Give the project a name and location > OK.
Step 2: Add a n
ew form to the project. Right click the project > Add > Windows Forms > Form2.cs > Add.
Step 3: Now in the Form1, drag and drop a button ‘btnOpenForm’ and double click it to generate an event handler. Write the following code in it. Also add the frm2_FormClosed event handler as shown below:
private void btnOpenForm_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.FormClosed += new FormClosedEventHandler(frm2_FormClosed);
frm2.Show();
this.Hide();
}
private void frm2_FormClosed(object sender, FormClosedEventArgs e)
{
this.Close();
}
When you close form in WinForms it disposes all of it's children. So it's not a good idea. You need to do it asynchronously, for example you can send a message to parent form.
The Form class doesn't provide any kind of reference to the 'parent' Form, so there's no direct way to access the parent (unless it happens to be the MDI parent as well, in which case you could access it through the MDIParent property). You'd have to pass a reference to the parent in the constructor of the child, or a property and then remember to set it, and then use that reference to force the parent to close.
Perhaps consider having the parent subscribe to an event on the child, and the child can fire that event whenever it wants to close the parent. The parent can then handle it's own closing (along with the child's).
You are clearly noo using the correct way to open and close forms. If you use any form of MVC or MVP this problem would not arise.
So use a form of MVP or MVC to solve this problem.
I agree with davidg; you can add a reference to the parent form to the child form's constructor, and then close the parent form as you need:
private Form pForm;
public ChildForm(ref Form parentForm)
{
pForm = parentForm;
}
private closeParent()
{
if (this.pForm != null)
this.pForm.Close();
this.pForm = null;
}
There's a very easy solution to that.
Problem (To be sure) : Starts App(Main Form) > Open Child Form of Main Form VIA Button or Any Event > Closes (Main Form) But Child Form also Closes.
Solution :
Use : Process.Start("Your_App's_EXE_Full_Path.exe");
Example : Try this to get full path:
string FullPath = Environment.CurrentDirectory + "\\YourAppName.exe";
Process.Start(FullPath);.
this.Close();
this way you'll get to keep every form you want to keep open.