Timer that closes modal form in Windows Forms - c#

I have several modeless Forms with grids to show data in a MDI Windows Forms app (Form1 in example code). Each of these Forms has a button that opens another Form as modal using ShowDialog (Form2 in example code), which allows editing the data shown in Form1. These modal forms have a Cancel button that closes the form without saving changes by setting DialogResult to Cancel.
I want to implement a timer that closes all forms after a certain time has elapsed from user login into the app. The problem arises if the timer triggers while a modal Form is open.
public partial class Form1 :
{ //Constructor ommited for brevity
private void btnEditData_Click(object sender, EventArgs e)
{
var form2 = new Form2();
timer1.Tick += (_, __) =>
{
form2.DialogResult = DialogResult.Cancel;
this.Close(); // this executes before form2.ShowDialog returns
};
timer1.Interval = 5_000;
timer1.Enabled = true;
form2.ShowDialog();
reloadData(); // here Form1 is already disposed because Close
// has been called on it. An exception is thrown as a consequence
}
private void reloadData()
{
if (this.IsDisposed)
// simulate using a disposed form
throw new ObjectDisposedException(this.Name);
}
}
My intention would be to somehow schedule the call Form1.Close() after form2.ShowDialog() has returned and the btnEditData_Click method has finished, so that there would be no danger of using a disposed form.

EDIT
After seeing your code, I'd suggest to introduce a third option in your Tick event handler that would be used to decide if you have to close the form. For example, Abort.
timer1.Tick += (_, __) =>
{
form2.DialogResult = DialogResult.Abort;
};
if (form2.ShowDialog() == DialogResult.Abort) Close();
else reloadData();
First thought: keep it simple and keep it clean. You don't have to keep track of your open forms, in an MDI application there is MdiChildren property of the parent form that will give you all the forms.
Then, to handle the closing part, you can use existing functionalities in .Net.
A difficult approach is using P/Invoke. You load Windows DLLs and use their functions to enumerate through all your forms and close which one you want. You can also simulate a click on Cancel button. You have to start with EnumWindows function (to identify the forms) and EnumChildWindows for form's children. I like this option because you have full control of your forms and controls, but can give you headaches if not familiar with the concept.
You can have a look at FormClosing event and subscribe to it. This event fires before the form is closed and you can make the clean up using it.
Using OOP - inheritance and override. Either you create a base form with custom close function that does the cleaning (and then inherit all your forms through it), or override form's close functions to achieve your goal.

Related

changing the control box on a form when exiting

Ok, I have a question about the Form Controlbox. I was wondering if it is possible to change or add what the exit button does on the form.
I can easily minimize, maximize and exit the form no problem. But this is what I am facing.
My app has an access login. After you log in it comes to the main form. I have a log out button when pressed, it goes back to the login form.
However, if you press the exit button, it exits the main form, and the program is still running, but with no way to bring the login form up.
So what I am trying to do is, when the main form is exited through the red X I want it to go to the login.
I can go the complex route: borderless form, movable form, custom buttons and etc., etc.,
I think it would be easier to change or add the exit button to return to the login form. Is this possible?
Move the logic out of your button click event, into a separate method.
private void button1_Click(object sender, EventArgs e)
{
OverrideFormExit();
}
private void OverrideFormExit()
{
// execute the code that was previously in button1's click event
}
Now you can subscribe that same method to your Form's Closed event, so that it executes when the user closes the Form.
For example, place the following in your Form's constructor:
FormClosed += (s, e) => OverrideFormExit();
Alternatively, you can subscribe to the main Form's Closed event from within the Login Form, when you instantiate the main Form. I'm guessing at what your code looks like here, obviously.
private void ShowMainForm()
{
FormMain frmMain = new FormMain();
frmMain.Show();
frmMain.FormClosed += (s, e) => this.Show();
this.Hide();
}

Multiple UIs in a Windows Forms program

I have a window forms project, I have a login screen, a menu and a couple of other forms, I'm switching between them with:
this.Hide();
frm.FormClosed += new FormClosedEventHandler(subFormClosed);
frm.Show();
and the FormClosedEventHandler(subFormClosed);
private void subFormClosed(object sender, FormClosedEventArgs e)
{
this.Close();
}
So the aim of this is that when a subform is closed by the user to close this.
There is however a problem, I want to go back to the menu and the issue is that I have one of two possibilites, that I can see:
I can pass the menu form to the subForm by reference to then show it and hide the subform - this seems to be one really really kludgy way of doing it but it would work.
I can just open a new version of the menu form - this would lead to huge memory issues in overuse (more instances being created and then never destroyed until the program is closed, e.g. 30 menu forms sub forms)
I was trying to use the CloseReason to check if the sub form was closed by the user or if it was closed from code, however both the exit button and this.Close() return CloseReason.UserClosing. Hence I couldn't differentate between the two types of exiting.
So basically what I'm asking for is there a better way of doing this, I've read about MDI and SDI and I can't really work out which would be applicable, or if the kludgy option 1 is the best way of doing this.
You can use ShowDialog and pass the menu page as the Owner. Something like this:
In Menu:
// on menu navigation button click
this.hide();
SubForm sub = new SubForm();
sub.ShowDialog(this); // open as a dialog with this form as the owner
In Sub Form:
// on subform's back button click or better, in the FormClosing event
this.Owner.show();
this.Close(); // this line is not needed if implemented in FormClosing event
Although the answer with the ShowDialog solution is a very good one, here is another way to do if for whatever reasons one does not want to use ShowDialog:
In the constructor of your menu form, use the FormClosed and the Shown events of your subforms this way:
subForm1.FormClosed += (s, e) => showMenu();
subForm1.Shown+= (s, e) => hideMenu();
subForm2.FormClosed += (s, e) => showMenu();
subForm2.Shown+= (s, e) => hideMenu();
...
void showMenu()
{
this.Show();
}
void hideMenu()
{
this.Hide();
}
Then you can use subForm1.Show() freely and close them the way you want: the events will be triggered accordingly.

c# How to allow some lower level forms to be acessible despite active form called by ShowDialog() method?

i got application, it shows 3 forms: log window, status window, and option window, option window calls some other forms and some of these forms are required to be called using ShowDialog() to return dialog result value for further decision making.
Using ShowDialog() raises problem, cause form called by that method excluding other forms from being accessible.
So my problem is i would like to be able to make atleast log window accessible no matter how much other forms has been called. Is there a way to make log window to independent form other forms, or could it be taken over by form called as last?
EDIT:
I failed to mention that the behaviour provided by ShowDialog() is quite usefull in my app and that i only lack ability to free that one or two forms from being locked. Switching to Show() is not option I'm considering as best while other forms, that are parent to form called by ShowDialog() are required to be still locked.
You'll have to make a choice between the two.
Use ShowDialog(), so that your parent form pauses execution, and only resumes when the second form is closed, or
Use Show(), so that your parent form continues execution after displaying the second form.
If you want to take some action, or read values from the second form when it's closed, then subscribe to its Closed event before you show it.
Let's assume your second form has a "First Name" TextBox, and a property to return that value:
public string FirstName
{
get { return yourFirstNameTextBox.Text; }
}
In your first form, you can subscribe to an event to take some action when the event occurs, like this:
var f2 = new SecondForm();
f2.Closed += (s, e) => MessageBox.Show(f2.FirstName);
f2.Show();
Now the user can continue on their way with both forms, and when the second form is closed, a message box will display the value of the "First Name" TextBox.
You'll probably want to do something more meaningful than this. Instead of displaying a message box, you could update a field on the first form, or take some any other action or set of actions that you want.
f2.Closed += (s, e) =>
{
MessageBox.Show(f2.FirstName);
nameLabel.Text = f2.FirstName;
// another action
// yet another action
};
So I now modeled your situation and found solution:
Use Show() instead ShowDialog() for dialog.
Write some code in the dialog to close it window after press the button (Ok, Cancel etc.). Because auto-closing works with ShowDialog only. But you don't need to set DialogResult manually.
At the Options form subscribe to FormClosed event to get DialogResult. For example:
dlg.FormClosed += (o, a) => { this.Text = dlg.DialogResult.ToString(); };
Handle Activate event of Options form to prevent focusing on parent window when dialog is opened:
if (dlg != null) dlg.Focus();
This solution has some difference with system behaviour of ShowDialog, but it works good.

How to force close button to terminate all childforms?

I have program that opens subwindows inside of it (mdi.parent). I have made component that is in one window under it, however, i want that that window never actually disposed after its created because i want to keep only one instance of it.
This can be made with code:
// This prevents this window disposing from window close button, we want always show one and only
// one instance of this window.
FormClosing += (o, e) =>
{
Hide();
e.Cancel = true;
};
However, after this there is problem, closing program requires pressing close button press twice. First press closes subwindow and second terminates program. How this can be get around?
I am working with Winforms.
As Habib said, you can call Application.Exit, but:
The Form.Closed and Form.Closing events are not raised when the
Application.Exit method is called to exit your application
If this is important to you, you can do something like this (MDI parent code):
private Boolean terminating;
protected override void OnClosing(CancelEventArgs e)
{
if (!terminating)
{
terminating = true;
Close();
}
base.OnClosing(e);
}
Call Application.Exit() in the form close event.
Application.Exit - MSDN
Informs all message pumps that they must terminate, and then closes
all application windows after the messages have been processed.
The code inside of your FormClosing event handler method is a bit too terse. It does its job of preventing the user from closing the form, but as you've also noticed, it prevents you from closing the form programmatically as well.
This is easily solved by testing the value of the CloseReason property of the FormClosingEventArgs that are passed in each time the event is raised.
These will tell you the reason why the form is attempting to close. If the value is CloseReason.UserClosing, then you want to set e.Cancel to true and hide the form. If the value is something else, then you want to allow the form to continue closing.
// This prevents this window disposing when its close button is clicked by the
// user; we want always show one and only one instance of this window.
// But we still want to be able to close the form programmatically.
FormClosing += (o, e) =>
{
if (e.CloseReason == CloseReason.UserClosing)
{
Hide();
e.Cancel = true;
}
};
Use This
Form[] ch = this.MdiChildren;
foreach (Form chfrm in ch)
chfrm.Close();
You can use Application.Exit if there is no processing happening when the application is closed. Otherwise, you can check Application.OpenForms collection in MDI parent's closing event and close all the other forms that are open.

To close C# Forms Application

I have 2 forms ...when i start the application..and use the close "X" from the title bar the entire application closes...now when i select an option from the 1st form in my case it is a button "ADD" as its a phonebook application..it goes to the 2nd form as i have used 1stform.hide() and 2ndform.show()...now when i do "X" from the title bar it doesnt shutdown completely as the 1stform is not closed....how to program it in such a way tht any stage the entire application should close
Your first form is set as the startup form. That means whenever it gets closed, your entire application is closed. And conversely, your application does not close until it gets closed. So when you hide the startup form and show the second form, the user closing the second form does not trigger your application closing because they have only closed a secondary, non-modal dialog.
I recommend changing your design so that the startup form is also the main form of your application. No sense trying to work around built-in functionality that can actually be useful. You want the application to quit when the main form is closed, no matter what other child forms are opened.
But the quick-and-dirty solution in your case is to make a call to Application.Exit. That will close all of the currently open forms and quit your application immediately. As I said just above, I don't so much recommend this approach because having to call Application.Exit from every form's FormClosed event handler is a sign that something has gone seriously wrong in your design.
If the single startup form paradigm doesn't work out for you, you should look into taking matters into your own hands and customizing the Main method in your Program.cs source file. See the answers given to this related question for some ideas on how that might work for you.
What you can do is to use the Form's FormClosing event, and add the following code:
Application.Exit();
This will stop the entire application, and close all windows. However, if a background thread is running, the process itself will survive. In this case you can use:
Environment.Exit();
Add a Application.Exit on every forms's Closing event
like this:
Create an closing event handler first
private void Form_ClosingEventhandler()(object sender, CancelEventArgs e)
{
//Perform any processing if required like saving user settings or cleaning resources
Application.Exit();
}
then bind this event to any form you create.
//Where you create new form and show it.
Form1 frm= new Form1();
//set other properties
frm.Closing += new EventHandler(Form_ClosingEventhandler);
Form2 frm2= new Form2();
//set other properties
frm2.Closing += new EventHandler(Form_ClosingEventhandler);
Surely you don't want to shut down the entire application after the user adds a phone number? You just need to make sure that your main window becomes visible again. Write that like this:
private void AddButton_Click(object sender, EventArgs e) {
var frm = new AddPhoneNumber();
frm.StartPosition = FormStartPosition.Manual;
frm.Location = this.Location;
frm.Size = this.Size; // optional
frm.FormClosing += delegate { this.Show(); };
frm.Show();
this.Hide();
}

Categories