I am having a bit of a head scratch moment at the moment.
I have 2 Forms one being a Parent form and other being the child form.
Inside the Parent form am trying to detect the FormClosed event which I have subscribed to a event inside the Parent form to handle some behaviour.
However the FormClosed event never gets subscribed or triggered when I close the child Form? I am not really sure what am doing wrong? I have even tried FormClosing and nothing gets triggered?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (var frm2 = new Form2())
{
frm2.ShowDialog();
frm2.FormClosed += Frm2OnFormClosed;
}
}
private void Frm2OnFormClosed(object sender, FormClosedEventArgs e)
{
// Implement further behaviour handling.
}
}
ShowDialog() will block execution of code in the calling method until the new form is closed. So you are opening and closing frm2 before you've ever even assigned the EventHandler. Switch your two lines around like so and you should be good to go:
frm2.FormClosed += Frm2OnFormClosed;
frm2.ShowDialog();
Related
I'm actually building a program at school in .net. This is the first time I'm using it and I've got a question.
I got a MdiParent form which has two MdiChildren : a config form, and an articles list form. When you fill the config form, the articles list should appear.
I'm actually using this method in the MdiParent :
private void MotherMdi_MdiChildActivate(object sender, System.EventArgs e)
{
...;
}
But I need to deal with multiple variables because this method is fired every time a mdichild is open or closed ...
Is there a way to call a specific method only when One MdiChild is closed ?
Thanks a lot.
Event FormClosing is raised every time a windows form is closed. You can handle that event and do processing that you need to do when a form is closing.
In the parent form:
private void ParentForm_Load(object sender, EventArgs e)
{
ChildForm childForm = new ChildForm();
childForm.childFormClosed += childForm_childFormClosed;
}
void childForm_childFormClosed(object sender, FormClosedEventArgs e)
{
// Handle processing here
}
In the child form:
public event EventHandler<FormClosingEventArgs> childFormClosed;
private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (childFormClosed != null)
{
childFormClosed(sender, e);
}
}
You may want to use FormClosed if that is what you need.
You can also use your own inherited class if you want to pass more information than what FormClosingEventArgs would do.
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.
I have two forms, mainForm and subForm. When mainForm loses focus I want subForm to disappear and then reappear as mainForm regains focus. I'm using the Activated and Deactivate events on the mainForm to keep track of whether mainForm has focus or not. When the Activated is fired I do subForm.Show() and the opposite for Deactivate. The problem I have is that when subForm gains focus mainForm disappear because I don't know how to say programmatically "make subForm disappear when the mainForm's Deactivate event fires except if it's because the subForm gained focus. The whole point of what I'm doing is to make both windows disappear when the mainForm loses focus because the user clicked on another application or use ALT+TAB to switch. I don't want to leave the subForm behind. Is there any way of checking as the Deactive fires whether it was because another form belonging to the application gained focus as opposed to some other application?
class MainForm : Form
{
SubForm subForm = new SubForm();
private void mainForm_Activated(object sender, EventArgs e)
{
this.subForm.Show();
}
private void mainForm_Deactivate(object sender, EventArgs e)
{
this.subForm.Hide()
// I need some logic to make sure that it is only hidden
// when the mainForm loses focus because the user clicked
// some other application in the taskbar and not when the
// subForm itself gains the focus.
}
}
This works on my machine.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private Form2 _form2;
private void Form1_Load(object sender, EventArgs e) {
_form2 = new Form2();
_form2.Show();
HandleFocusEvents();
}
private void HandleFocusEvents() {
this.LostFocus += Form_LostFocus;
_form2.LostFocus += Form_LostFocus;
this.GotFocus += Form_GotFocus;
}
private void Form_LostFocus(object sender, EventArgs e) {
if (!_form2.ContainsFocus && !this.ContainsFocus) {
_form2.Hide();
}
}
private void Form_GotFocus(object sender, EventArgs e) {
if (!_form2.Visible) {
_form2.Show();
}
}
}
In your main forms code, where you create an new instance of the sub form, add an event that is fired whenever the instance of the sub form form is activated. In the event handler for it set a bool variable to true. Now, do the same, for the deactivate event of the sub forms instance, except set the bool variable to false.
Now in the event for the main form loosing focus, before hiding it check that bool variable and make sure it is false "the sub form doesn't have focus" and only then would you hide the main form.
I could provide code if I could see what you have so far. There are a lot of different ways you could to this.
Hope this helps you!
If I understand it correctly, this sounds like just a normal MDI application. Can you make your main form the MDI Parent and set the sub form MDI parent to the main form? Most of these activation stuff that you are talking about should then be look after automatically? Or at most just trap the minimize event in the subform to then also minimize mdi parent form
I have a WinForm that I create that shows a prompt with a button. This is a custom WinForm view, as a message box dialog was not sufficient.
I have a background worker started and running. I also want to exit the while(aBackgroundWorker.IsBusy) loop if the button on myForm was clicked.
//MyProgram.cs
using(CustomForm myForm = new CustomForm())
{
myForm.Show(theFormOwner);
myForm.Refresh();
while(aBackgroundWorker.IsBusy)
{
Thread.Sleep(1);
Application.DoEvents();
}
}
Right now, in the CustomForm the Button_clicked event, I have
//CustomForm.cs
private void theButton_Click(object sender, EventArgs e)
{
this.Close();
}
Do I need to add more code to the CustomForm class, or the location where I declare and initialize the form in order to be able to detect a closure?
To detect when the form is actually closed, you need to hook the FormClosed event:
this.FormClosed += new FormClosedEventHandler(Form1_FormClosed);
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// Do something
}
Alternatively:
using(CustomForm myForm = new CustomForm())
{
myForm.FormClosed += new FormClosedEventHandler(MyForm_FormClosed);
...
}
void MyForm_FormClosed(object sender, FormClosedEventArgs e)
{
// Do something
}
You might be going overkill. To show a form like a dialog window and wait for it to exit before returning control back to the calling form, just use:
mySubForm.ShowDialog();
This will "block" the main form until the child is closed.
Make sure your background worker supports cancellation and as others have pointed out use the form closed event handler. This code should point you in the right direction:
using(CustomForm myForm = new CustomForm())
{
myForm.FormClosed += new FormClosedEventHandler(ChildFormClosed);
myForm.Show(theFormOwner);
myForm.Refresh();
while(aBackgroundWorker.IsBusy)
{
Thread.Sleep(1);
Application.DoEvents();
}
}
void ChildFormClosed(object sender, FormClosedEventArgs e)
{
aBackgroundWorker.CancelAsync();
}
Handle the FormClosing event of the form to be notified when the form is closing, so you can perform any cleanup.
You should be able to hook into the FormClosing and FormClosed events.
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.formclosing.aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.formclosed.aspx
Closing is before it's closed.
Closed is after it's closed.
A couple things...
First, it appears that loop is there in order to prevent execution form proceeding while the dialog is open. If that is the case, change you .Show(parent) to .ShowDialog(parent). That will also take care of the rest of your question.
Note that this.Hide(); is not the same as this.Close(); in the actual dialog your overriding the closed event
I have got the new problem with opening and closing form in C#.
My problem is how to dispose the form after closing .
here is my code :
Program.cs:
static class Program
{
public static Timer timer;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
timer = new Timer { Interval = 1000};
timer.Start();
Application.Run(new Form1());
}
}
Form1.cs:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 form = new Form2();
form.ShowDialog();
/// I've tried Dispose() method . but didn't work
}
}
Form2.cs:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
Program.timer.Tick += timer_Tick;
Close();
// I've tried Dispose() method instead of Close() but didn't work
}
private int count = 0;
void timer_Tick(object sender, EventArgs e)
{
count++;
if (count == 5) MessageBox.Show("");
}
}
Edited :
My question is : why the message box shows after 5 seconds when the form2 has closed!
This question turns out to be about Dispose.
Firstly, Dispose has nothing to do with garbage collection. The following happens:
You have a global Timer instance
You create form2
Form2 subscribes to the timer
Form2 is Closed and/or Disposed
The Timer event fires, increments the counter and shows a MessageBox
The Timer event keeps firing until the App closes.
The main point to understand is that Close/Dispose only change the status of the Form, they don't (can't) 'delete' the instance. So the (closed) form is there, the counter field is still there and the Event fires.
OK, part 1:
A using () {} block would be better but this should work:
private void button1_Click(object sender, EventArgs e)
{
Form2 form = new Form2();
form.ShowDialog();
/// I've tried Dispose() method . but didn't work
form.Dispose(); // should work
}
If not, please describe "doesn't work".
private void Form2_Load(object sender, EventArgs e)
{
Program.timer.Tick += timer_Tick;
Close();
/// I've tried Dispose() method instead of Close() . but didn't work
}
This is strange, but I'll assume that it is artifical code for the question.
Your global Program.Timer now stores a reference to your Form2 instance and will keep it from being collected. It does not prevent it from being Disposed/Close so your timer will keep firing for a Closed Form, and that will usually fail and cause other problems.
Don't do this (give Form2 it's own timer)
Use a FormClosed event to unsubscribe: Program.timer.Tick -= timer_Tick;
The simplest and most reliable way to dispose a Form after using is to put the usage inside of a using block
using (Form2 form = new Form2()) {
form.ShowDialog();
}
The using block in C# is a construct that essentially expands the above into the following code.
Form2 form;
try {
form = new Form2();
...
} finally {
if ( form != null ) {
form.Dispose();
}
}
This is an old question but it touches some interesting points about how objects work. A form is, essentially, an object. All objects of the same class share the same methods but each one has their own data. What does this mean? This means that, closing or disposing an object does not free/delete/remove any code from the memory. Only data. All that was about objects in general, no matter the language.
Now, specifically about your code. Let us examine what the line Program.timer.Tick += timer_Tick; does. This gives a pointer to your function in the Form object to the timer object. So, now, no matter what you do to the Form object, the timer object will keep calling that function. The timer object does not care about your Form and is not even aware of the existence of the Form object. It only cares about the function you passed the pointer to. As far as the timer object is concerned, this function is a standalone function.
What does Form.Close() do? Form.Close() disposes the resources used by the form, aka, marks the form's controls for garbage collection unless the form is displayed using ShowDialog. In that case, Dispose() must be called manually. MSDN
Needless to say(or maybe not so needless) that if closing/disposing the form cleared the function from the memory, the timer object would have an invalid pointer and your program would crash after 5 seconds.
Perhaps I am reading the question wrong, but I think the gentlemen needs to know that, to close a form (say form2) opened as Form2.ShowDialog(), you need to set Form2.DialogResult within Form2. Just setting that member is all it takes to close the form and return the result.
form.ShowDialog() shows the form as a modal dialog. This means that the call doesn't return until the Form is closed.
Note that clicking the close X on a modal dialog doesn't close the form, it just hides it. I am guessing that this is what confuses you.
If you want the code in form1 to continue executing rather than blocking, you should call Show() instead of ShowDialog(). Non-modal will close when the X is clicked.
If you do want a blocking modal dialog, you should surround the form with a using block as described in the other answers.
When building a modal dialog you would typically add an "OK" button or similar and set the AcceptButton property of the form to that button to allow the user to close the form by pressing enter. Similarly you can add a "Cancel" button and set the CancelButton property to capture the Esc key.
Add a click handler to the two buttons, set the DialogResult property of the form accordingly and call Close().