Form.Close() doesn't close window immediately - c#

I need to be sure that all forms will be closed if I user somehow close the main form. So I decided to hide Close() function and I wrote something like this
public new bool Close()
{
List<Form> formsList = new List<Form>(Application.OpenForms.Count);
foreach (var form in Application.OpenForms)
formsList.Add((Form)form);
formsList.Reverse();
foreach (var form in formsList)
{
if (form.IsDisposed) continue;
Invoke(new GenericEventHandler(() => { form.Close(); }));
}
return Application.OpenForms.Count == 0;
}
So if all forms are closed successfully I return true and thanks to this I know that user can be logged out from application.
However it seems that form.Close() function is not fired immediately. After calling form.Close() formclosed event is not fired immediately as well as collection Application.OpenForms is not modified. The amount of opened forms is noted changed.
What could be a reason?

Why dont you just use
Application.Exit();

If the main form is created in the standard way in WinForms via Application.Run(new MainForm()); then when it closes, the application will exit, closing all other forms. So I don't think you need to worry about manually closing all child forms.

Well, if the user closes the main form, then upon closing it your application will definitely exit, since your form is responsible for keeping the application alive when it was passed to Application.Run().
Now, if you want to close your app when a form is closed, in that form you do this:
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
Application.Exit();
}

In general I suggest not to use new keyword to any members of Control and/or Form class. If you need your own method use your own name of it.
In your code you don't really need to use Invoke when closing child forms.
If you need to be sure that all your child Forms are closed before you actually execute logic related to MainForm.Close you may subscribe to MainForm.FormClosing event with handler similar to follwing:
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
bool allChildFormsClosed = !closeAllChildForms();
if(!allChildFormsClosed)
{
e.Cancel = true;
MessageBox.Show("Failed to close one or more child forms.");
// better add some yes|no dialog to force exit if necessary
}
}
You may implement closeAllChildForms in a similar way as you did above - just: remove the formsList.Reverse(); and the 'Invoke..' - just call Close directly and update: if (this == form || form.IsDisposed) continue;

If for some reason, Application.Exit isn't cutting it for you, you could try this code (put it in the form's FormClosing event):
while (Application.OpenForms.Count > 1)
{
Form form = Application.OpenForms[1];
form.Close();
while (!form.IsDisposed) Application.DoEvents();
}
or if you want it to time out if it can't close all the forms after a while,
while (Application.OpenForms.Count > 1)
{
Form form = Application.OpenForms[1];
form.Close();
DateTime StartTime = DateTime.Now;
while (!form.IsDisposed && (StartTime - DateTime.Now).TotalMilliseconds < 1000)
Application.DoEvents(); //1-second timeout here
if (!form.IsDisposed) //if it's still not closed
{
//send a message to the user that the application can't exit right now
mbox("some forms are still open or whatever");
e.Cancel = true;
return;
}
}

Related

Restart a form that is disposed

A parent form starts a child form that embeds the Chromium Embedded Framework through CEFSharp. The user has the ability to close the child form. The user can restart the child form. When that happens the application terminates.
if (debugForm == null || debugForm.IsDisposed)
{
if (debugForm != null && debugForm.IsDisposed)
{
debugForm = null;
}
debugForm = new CEFdebugger();
....
}
When the form has been run once, closed and restarted the line
debugForm = new CEFdebugger();
causes the application to terminate. In the output window I have
... has exited with code 1073741855 (0x4000001f)
According to this thread this indicates I should enable debugging unmanaged code, but that doesn't seem to have helped.
In the FormClosing event for CEFdebugger I have added
private void CEFdebugger_FormClosing(object sender, FormClosingEventArgs e)
{
Cef.Shutdown();
this.Controls.Remove(browser);
browser = null;
}
That hasn't helped. The application just terminates.
The problem was caused by the statement
Cef.Shutdown();
in CEFdebugger_FormClosing. By commenting it out and testing Cef.IsInitialized in the form constructor the problem was resolved.

Disallowing interaction with background form

On my application's first run, two forms open. The topmost form needs to take priority, and disallow any interaction with the form in the background. I have tried ShowDialog() as referenced here, however this hides the form in the background which I do not wish to do. Is there a method of accomplishing this?
public Form1()
{
InitializeComponent();
if (!fileexists(#"c:\Management Tools\Absence Tracker\bin\data\tbase.skf"))
{ firstrunactions(); }
}
void firstrunactions()
{
//open the get-started form and invite user to populate serialisable objects
firstrun frwindow = new firstrun();
frwindow.ShowDialog();
}
When you are using .ShowDialog() the execution of the containing method is paused until you close the newly opened window. So make sure to do everthing else before you call .ShowDialog(). Otherwise your program gets stuck in this method. If you are calling .ShowDialog() before the background window is shown will cause problems.
But using .ShowDialog() here is totally correct and has the right functionality.
Example how not to do it (causes the same behavior like in your problem):
public Form1()
{
InitializeComponent();
//this is the wrong place for showing a child window because it "hides" its parent
Form frwindow = new Form();
frwindow.ShowDialog(this);
}
The magical place where it works:
private void Form1_Shown(object sender, EventArgs e)
{
Form frwindow = new Form();
frwindow.ShowDialog(this);
}
Edit: In your case it is enough moving if(!fileexistst...) into the Form1_Shown()-event.
Try with frwindow.ShowDialog(this);
Or instead "this" pass the other form as parameter.
Also move this part if (!fileexists(#"c:\Management Tools\Absence Tracker\bin\data\tbase.skf"))
{ firstrunactions(); }
}
in OnLoad override.

Closing a form when other form is opening

The current problem is that the "mainScreen" shows up, but immediately closes, I have no idea why. This is the piece of code that handles the close and opening of the new form.
Edit: .this refers to Login.cs (sorry)
if(templogin == true && permission.Equals("1"))
{
mainScreen.IsAdmin();
this.Close();
mainScreen.ShowDialog();
}
I think, you should not close the application(as login form seems main form while you started the application) before another form run:
Do like this :
if(templogin == true && permission.Equals("1"))
{
this.Hide();
mainScreen.IsAdmin();
mainScreen.ShowDialog();
this.Close();
}
If you want to run 2 forms (switching from one to another), then you should do it in Main
Instead of
Application.Run(new StartupForm());
you'll have to use
var startup = new StartupForm();
startup.ShowDialog();
if(somecondition) // when StartupForm is closed and return something (or property is set, etc)
{
var main = new MainScreen();
main.ShowDialog();
}
You don't need Application.Run at all when using ShowDialog.

how to get the output from windows form to a class?

i want to get output from windows form to the calling application that is class library.
Form1 f=new Form1();
f.Show();
if(f.PropertyName!=null)
{
}
f.PropertyName is always null. After f.show() it straightaway runs if ()condition. is there any way to get the output from the form1?
Show() is non-blocking. Use ShowDialog(), or add a delegate to the FormClosing() event of your form and move your if() logic into it.
EDIT:
Here's an example of a lambda "delegate" for the closing event:
f.FormClosing += (sender, e) => { /* your code here */ };
EDIT 2: Whether you handle FormClosed() or FormClosing() depends on your specific needs. If you want to cancel the form close on a "bad" value, FormClosing() lets you cancel the form close.
You probably want to use ShowDialog() instead of Show().
Form1 f=new Form1();
if(f.ShowDialog() == DialogResult.OK)
{
if(f.PropertyName!=null)
{
}
}

Managing the dialog form in C#

On click of a button I have this code with is should show a dialog on top of the current form and display text, wait for one second, change the text and then finally close it:
Form p = new Form();
p.ShowDialog();
p.Text = "Start.";
Thread.Sleep(1000);
p.Text = "Counting.";
Thread.Sleep(1000);
p.Text = "End.";
Thread.Sleep(1000);
p.Close();
However once it executes p.ShowDialog(); it stops the code until the form p is closed and it doesn't work as I intended it to. Can I get some guidance on this? Not necessarily the solution, but at least maybe some keywords I could google on?
UPDATE: due to the difficulties I am facing trying to access business logic, which is irrelevant to the problem, I am delaying providing the working example. Stay tuned and sorry :)
SOLUTION: what I did is in fact used Show() instead of ShowDialog(). Since i was impossible to access form from business logic, BackgroundWorker came in handy and was being used between them. I cannot share any code or the layout of the project structure, but in conclusion, the accepted answer's main statement was the key to the solution :)
That is the point of ShowDialog(). It creates a modal form and does not return control to the calling function until you are done. If it doesn't need to be modal, then use .Show(). If it does need to be modal, then put code in the Form Load method to update the text as needed.
http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx
taken from the link above:
When this method is called, the code following it is not executed until after the dialog box is closed.
if you want to form to display whatever it is you want to display you should write the code inside the the form itself, do that in an eventhandler of the form show event.
As you have found, ShowDialog is a blocking method that does not return until the dialog is closed. Your code to change the text and handle the delay needs to be within the dialogue itself.
However, it's worth noting the next problem that you'll find: if you call Thread.Sleep(1000) from the UI thread, your application will become unresponsive for 1 second at a time. This is probably not what you're aiming for! I'd suggest you look into the Timer or BackgroundWorker classes to handle this more smoothly.
Check this out:
public partial class Form2 : Form
{
delegate void SetTextCallback(string text);
delegate void CloseFormCallback();
public Form2()
{
InitializeComponent();
new Thread(DoMagic).Start();
}
public void DoMagic()
{
this.SetText("Start.");
Thread.Sleep(1000);
this.SetText("Counting.");
Thread.Sleep(1000);
this.SetText("End");
Thread.Sleep(1000);
this.CloseForm();
}
private void CloseForm()
{
if (this.InvokeRequired)
{
CloseFormCallback c = new CloseFormCallback(CloseForm);
this.Invoke(c);
}
else
{
this.Close();
}
}
private void SetText(string text)
{
if (this.label1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.label1.Text = text;
}
}
}

Categories