Change LayoutMdi from parent form when closing child form - c#

I'm currently working with forms and mdi. In my project there is a mainform (a mdiContainer) which can have x subforms. I want to reach, that everytime, a subform is closed, all other subforms where arranged again.
You can do that with writing this into the mainform:
public void resetToolStripMenuItem_Click(object sender, EventArgs e)
{
this.LayoutMdi(System.Windows.Forms.MdiLayout.TileVertical);
}
In the subform, i do this:
private void subform_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
Form1 mainform = new Form1();
mainform.resetToolStripMenuItem_Click(mainform, EventArgs.Empty);
}
catch
{
System.Windows.Forms.MessageBox.Show("error");
}
}
It does not give any error, but also wont arrange the subforms again. I also tried to call the method with other parameters.
Any idea how i can make this work?

This line should make you pause:
Form1 mainform = new Form1();
You made a new form, so you aren't referencing the existing one.
But I think there are issues trying to do this from the child form.
It's probably better to listen to the Closed event of the child from the MDIParent, like this:
ChildForm childForm = new ChildForm();
childForm.FormClosed += childForm_FormClosed;
childForm.MdiParent = this;
childForm.Show();
And then in the Closed method, call the code:
void childForm_FormClosed(object sender, FormClosedEventArgs e) {
this.BeginInvoke(new Action(() => {
resetToolStripMenuItem_Click(null, null);
}));
}
I used BeginInvoke because otherwise, the closed child form is still being included in the layout tiling.

Related

Form remains open in the background after closing it

So I want to show a form and close the other form so I did this in form1:
private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
{
Form2 x = new Form2();
x.Show();
this.Hide();
//didn't use this.close() cause then the whole program will close instead of
//just the current form
}
Then I wanted the main form to open again after the 2nd form is closed so I did this in form2:
private void Form2_FormClosed(object sender, FormClosedEventArgs e)
{
Form1 x = new Form1();
x.Show();
}
Now the problem is when I'm back in the main form after closing the 2nd form. if I close the main form it doesn't fully close and still remains open in the background (I found it in task manager). I think its because the "show" method just opens another form instead of making the form appear so the main form which got hidden is still there running in the background.
what should I do to make the form get closed when I exit it?
I tried putting this.close(); in the form closed and form closing event but both resulted in a crash.
When you write:
Form1 x = new Form1();
you are creating a new Form1 object, so x refers to the new one and not to the original one. Instead, you could use this:
private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var form2 = new Form2())
{
this.Hide();
form2.ShowDialog();
}
this.Show();
}
When ShowDialog() is called, the code following it is not executed until after the dialog box is closed, so this.Show() will execute only after Form2 is closed.
Another option is to simply subscribe to the FormClosed() event of Form2 when you create it, then un-hide your instance of Form1 from there:
// ... all in Form1 ...
private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Hide();
Form2 x = new Form2();
x.FormClosed += X_FormClosed;
x.Show();
}
private void X_FormClosed(object sender, FormClosedEventArgs e)
{
this.Show();
}
So then when you close Form2 your instance of Form1 will automatically re-appear.

Having problem to close a second windows form without closing the main form

In a c# I have two forms: Windows Form1 & Windows Form2.
When I click on a link label in Form1, the second form is shown. But when I close the second form, my first Windows form closes too.
my main form is:
namespace Windows_Forms_Basics
{
public partial class ShowLocationOnMapForm : Form
{
private string latitude;
private string longitute;
private GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();
public ShowLocationOnMapForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
watcher = new GeoCoordinateWatcher();
// Catch the StatusChanged event.
watcher.StatusChanged += Watcher_StatusChanged;
// Start the watcher.
watcher.Start();
}
private void button_ShowOnMap_Click(object sender, EventArgs e)
{
textBox_Latitude.Text = latitude;
textBox_Longtitude.Text = longitute;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>")]
private void Watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) // Find GeoLocation of Device
{
try
{
if (e.Status == GeoPositionStatus.Ready)
{
// Display the latitude and longitude.
if (watcher.Position.Location.IsUnknown)
{
latitude = "0";
longitute = "0";
}
else
{
latitude = watcher.Position.Location.Latitude.ToString();
longitute = watcher.Position.Location.Longitude.ToString();
}
}
else
{
latitude = "0";
longitute = "0";
}
}
catch (Exception)
{
latitude = "0";
longitute = "0";
}
}
private void label1_Click(object sender, EventArgs e)
{
}
private HelpForm form2 = new HelpForm();
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Hide();
form2.Show();
}
}
}
and my second form is:
namespace Windows_Forms_Basics
{
public partial class HelpForm : Form
{
public HelpForm()
{
InitializeComponent();
}
private void HelpForm_Load(object sender, EventArgs e)
{
}
private void button_BackToForm1_Click(object sender, EventArgs e)
{
ShowLocationOnMapForm form1 = new ShowLocationOnMapForm();
this.Hide();
form1.Show();
}
}
}
Do you have any idea how to tackle this problem?
I am guessing you may be “confusing” forms and one forms “ability” to access another form. Let’s start by taking a look at the bit of code in your initial question…
private HelpForm form2 = new HelpForm();
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
this.Hide();
form2.Show();
}
This code is called from (what appears to be) a Form named ShowLocationOnMapForm.
On this form is a LinkLabel named linkLabel_help and its LinkClicked event is wired up and is shown above.
In addition, there appears to be a “global” variable form2 that is a HelpForm object (first line in the code above), which is another form. It is unnecessary to create this “global” form variable in the ShowLocationOnMapForm. …. However, we will continue as it is not pertinent at this point.
When the user “clicks” the `LinkLabel’ the above method will fire.
On the first Line in the method…
this.Hide();
Is going to “hide” the current form ShowLocationOnMapForm … and “show” the second form (HelpForm) with the line…
form2.Show();
On the surface, this may appear correct, however, there is one problem that I am guessing you are missing. The problem is…
How are you going to “unhide” the first form ShowLocationOnMapForm?
The second form (HelpForm) is “shown”, however, it isn’t going to KNOW anything about the first form. In this situation the first form is basically LOST and you have no way of accessing it. Therefore when you attempt the line… form1.Show(); in the second form, the compiler is going to complain because its not going to know what form1 is. In this code, there is NO way to get back to the first form. It is not only hidden from the user, but from the second form’s perspective the “CODE” can’t see it either!
Your faulty solution is not only “creating” another form1 but it is also doing the same with the second form.
Given this, it appears clear, that if you want to keep access to the first form… then you are going to have to use a ShowDialog instead of Show OR pass the first form to the second form.
Since it is unclear “how” you want these forms to interact, I can only proffer two (2) ways that you can use to at least keep access to the first form.
1) Use ShowDialog instead of Show when displaying the second form. It may look like …
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
HelpForm form2 = new HelpForm();
this.Hide();
form2.ShowDialog();
this.Show();
}
In the code above, the ShowDialog is going to “STOP” code execution in the first form and will wait for the second form (form2) to close. When executed, the first form is hidden, then the second form is shown, however, unlike the Show command, the line this.Show(); will not be executed until the second form closes. I am guessing this may be what you are looking for.
2) Pass the first form to the second form giving the second form “access” to the first form.
This will require that the second form has a “constructor” that takes a ShowLocationOnMapForm object as a parameter. With this, the second form can create a “global” variable of type ShowLocationOnMapForm that “points” to the first form. The constructor may look like…
private ShowLocationOnMapForm parentForm;
public HelpForm(ShowLocationOnMapForm parent) {
InitializeComponent();
parentForm = parent;
}
In the first form, you would instantiate the second form with...
HelpForm form2 = new HelpForm(this);
With this approach, the second form will have total access to the first form. You could add the “back” button as you describe and simply execute the line…ParentForm.Show(); However, I recommend you also wire up the second forms FormClose event and show the first form, otherwise, if the user clicks the close button (top right) and doesn’t click the “back” button, then you will have LOST the first form again.
Without knowing “exactly” how you want these forms to interact it is difficult to proffer a complete solution.
There are also other ways to accomplish this, however these should work for most cases.
I hope this makes sense and helps.
I tried to solve this problem by placing a 'Back to Form1' button in Form2. Which works, and the solution is as follows:
on my Form1 I have:
private Form2 form2 = new HelpForm();
private void linkLabel_help_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Hide();
form2.Show();
}
and on my second form I have:
private void button_BackToForm1_Click(object sender, EventArgs e)
{
HelpForm form1 = new HelpForm();
this.Hide();
form1.Show();
}
But the problem is if I click the close button (on top right of the window) instead of the GoBack button on the second form, Form1 & Form2 both close in the same time.

How do I respond to the last MDI childform closing?

My goal is to respond to the last child form of an mdi container closing(for example, close the parent itself or show something new). The problem I face is that the mdi container's MdiChildren collection still indicates that the container contains children.
The approach I have tried is
void childMdiForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (this.MdiChildren.Any())
{
//Do stuff
}
}
MdiChildren.Count() is still 1 after the last child form is closed.
I have the same results by trying to handle the parentform.MdiChildActivate event.
The MdiChildren collection appears to not be updated yet when the child form has closed. The same occurs when there are multiple children: it will still contain all the crildren, it appears to update the collection at a later moment.
Is this the right approach? If not, how can I get an accurate count of the number of mdi children after closing a form?
Yes, the MdiChildren property doesn't get updated until after the FormClosed event is delivered. There's a universal solution for event order issues like this, you can elegantly get code to run after event handing is completed by using the Control.BeginInvoke() method. This code solves your problem:
protected override void OnMdiChildActivate(EventArgs e) {
base.OnMdiChildActivate(e);
this.BeginInvoke(new Action(() => {
if (this.MdiChildren.Length == 0) {
// Do your stuff
//...
MessageBox.Show("None left");
}
}));
}
WinForms can be a little quirky at times and this is one example of that. I am not entirely sure why closing the last MDI child does not make the MdiChildren property return an empty array immediately thereafter.
In most cases you're going to be keeping track of either the children forms or the data models yourself, so I would simply keep a local array for managing this:
List<Form> childForms = new List<Form>();
void AddChildWindow()
{
var window = new ChildForm();
window.MdiParent = this;
window.Tag = Guid.NewGuid();
window.FormClosed += (sender, e) => { OnMdiChildClosed(sender as Form, e.CloseReason); };
window.Shown += (sender, e) => { OnMdiChildShown(sender as Form); };
window.Show();
}
void OnMdiChildShown(Form window)
{
childForms.Add(window);
Trace.WriteLine(string.Format("Child form shown: {0}", window.Tag));
Trace.WriteLine(string.Format("Number of MDI children: {0}", childForms.Count));
}
void OnMdiChildClosed(Form window, CloseReason reason)
{
childForms.Remove(window);
Trace.WriteLine(string.Format("Child form closed: {0} (reason: {1})", window.Tag, reason));
Trace.WriteLine(string.Format("Number of MDI children: {0}", childForms.Count));
if (childForms.Count == 0)
{
// Do your logic here.
}
}
Here's a full test example:
namespace WindowsFormsApplication1
{
public partial class mdiMainForm : Form
{
private List<Form> _children = new List<Form>();
public mdiMainForm()
{
InitializeComponent();
}
private void mdiMainForm_Shown(Object sender, EventArgs e)
{
Form3 f3 = new Form3();
f3.MdiParent = this;
f3.FormClosed += mdiChildClosed;
_children.Add(f3);
f3.Show();
Form4 f4 = new Form4();
f4.MdiParent = this;
f4.FormClosed += mdiChildClosed;
_children.Add(f4);
f4.Show();
Form5 f5 = new Form5();
f5.MdiParent = this;
f5.FormClosed += mdiChildClosed;
_children.Add(f5);
f5.Show();
}
private void mdiChildClosed(Object sender, FormClosedEventArgs e)
{
if (_children.Contains((Form)sender))
_children.Remove((Form)sender);
if (_children.Count == 0)
MessageBox.Show("all closed");
}
}
}
You're code seems to imply you are checking the child Form's MdiChildren collection. But perhaps I'm misreading your code.
Use the MDI Form's 'MdiChildActivate' event:
private void Form1_MdiChildActivate(object sender, EventArgs e) {
if(this.MdiChildren.Length == 0) {
// replace with your "Do Stuff" code
MessageBox.Show("All Gone");
}
}
I know this answer is very late, but I have a much simpler solution to the problem than what has been provided so I'll share it here.
private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
// If WinForms will not remove the child from the parent, we will
((Form)sender).MdiParent = null;
}

detecting in form1 that form2 is closed

I am trying to detect that form2 is closed in form1. I have this so far
private void AddStageBtn_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.ShowDialog();
if (form2.IsDisposed)
{
MessageBox.Show("it was closed!");
}
}
Any suggestions? Thanks again!
Adhere to the FormClosed event of form2.
Wherever you create it do:
form2.FormClosed += new FormClosedEventHandler(form2_FormClosed);
Then create the method:
void form2_FormClosed(object sender, FormClosedEventArgs e)
{
// Do whatever you want here
}
You'll also want to use .Show() instead of .ShowDialog() if you want to be able to use either form, otherwise form1 will be unavailable until form2 is closed (which I am assuming is not the behavior you are looking for).
In this particular situation
private void AddStageBtn_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.ShowDialog();
MessageBox.Show("it was closed!");
}
will work fine. If you want to be able to do action within form1 while form2 is open you need to use Show instead of ShowDialog. Then you can create a handler for form closed within form1.
form2.FormClosed += new FormClosedEventHandler(form2_FormClosed);

How to handle object disposed exception was unhandled Exception in c#?

I am working in c# windows application.I have two windows from name as form1 and form2.i am calling form2 by clicking the button in form1 but i create the object for form2 in constrcutor of form1 .if i click the button first time form2 showed successfully after that i close the form2 by clicking the default close button and again click the button now i am getting object disposed exception was unhandled exception.how can avoid this?
Don't handle the exception, fix the bug in your code. The form instance is dead after the form is closed, you cannot show it again. Either write it like this:
private void button1_Click(object sender, EventArgs e) {
var frm = new Form2();
frm.Show(this);
}
Or if you want only one instance of the form to be ever visible:
Form2 theForm;
private void button1_Click(object sender, EventArgs e) {
if (theForm != null) {
theForm.WindowState = FormWindowState.Normal;
theForm.BringToFront();
}
else {
theForm = new Form2();
theForm.FormClosed += delegate { theForm = null; };
theForm.Show(this);
}
}
You are keeping a reference to the object (window here) but you are closing it. Object is disposed but is not garbage collected. Your reference here is invalid now as the object has lost its usable state.
You need to hide the form instead of close if you need to re-use it. Or create a new instance to load it again.
You can use events in order to let form1 know when form2 has been closed and clear its reference to it. Then form1 doesn't need to call form2 if it has been closed.
We do something similar here with a few of our tools that plug into third-party apps. Code sample below:
public class Form1 : Form
{
private Form2 otherForm;
private void ActivateForm2_Click(object sender, EventArgs e)
{
if (otherForm == null || otherForm.IsDisposed)
{
otherForm = new Form2();
otherForm.FormClosed += new FormClosedEventHandler(otherForm_closed);
}
otherForm.Show(this);
}
private void otherForm_Closed(object sender, FormClosedEventArgs e)
{
otherForm.Dispose();
otherForm = null;
}
}

Categories

Resources