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();
}
Related
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.
Is it possible to detect a form closing from another form.
For example.
If I had a mainForm that opens subForm, can I detect within the mainForm that the subForm has closed and execute code?
I understand I could create an event handler within the subForm, but this is not really what I'm after because what I'm about to do after the subForm closes, is within the mainForm (changes to mainForm).
The FormClosed event is public, so you can create a handler from the main form.
//Inside main Form. Click button to open new form
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.FormClosed += F2_FormClosed;
f2.Show();
}
private void F2_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show("Form was closed");
}
Take a look at the public FormClosedEvent. Since the modifier is public, you're able to do something like the following example:
SubForm subForm = new SubForm();
subForm.FormClosed += delegate
{
MessageBox.Show("subForm has closed");
};
subForm.ShowDialog();
The above example creates a new form (of type SubForm), adds a new event handler to display a message box telling the user that the form has closed, and finally uses the ShowDialog() method which will prevent the user accessing the main form until the sub form has been closed.
The usual case for this is a "Modal Dialog" (like Message Box and its Family).
Every form can be opened as Modal Dialog, by using ShowDialog() isntead of Show().
Otherwise the event way is the only way.
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.
I have a modal form (login) which I want that on closing it using "X" button, force application to exit.
I tried this:
private void Login_Deactivate(object sender, EventArgs e)
{
Application.Exit();
}
this cause my application to exit on pressing login button (closing). but I want exiting only on "X" button of window.
How can I do it?
Get rid of the Deactivate event, you don't need it.
The issue here is that you cannot tell that just the "X" button was pressed. What you really do is track all the other conditions that cause form close, and react when it's not one of those.
You should leverage the DialogResult enum, and allow the calling application to react, instead of trying to do it all in the Login form.
In your Login form constructor, set the property to None:
public Login()
{
DialogResult = DialogResult.None;
}
Then, in your button handler for "OK", set the property: DialogResult = DialogResult.OK.
If you have a specific Cancel event, you can set DialogResult = DialogResult.Cancel.
Ultimately, you could have a FormClosing event in the Login form, in which you can check this value and react. But more importantly, your calling application can check the result from the login form and react there as well.
I think you should use this.Parent.Application.Exit(), it kills whole application.
Instead of Deactivate event, subscribe to the "FormClosing" event of the "Login" form. Write the same code which you have written.
Keep the Deactivate event only if it is required to close the Application when you click outside login form.
I have some problems with Form control focusing.
On form1 I click a button and run the code below:
private void btnTest_Click(object sender, System.EventArgs e)
{
form2 = new Form2();
Application.Idle += new EventHandler(Application_Idle);
form2.Show();
form2.Activate();
form2.textBox1.Focus();
Form3 form3 = new Form3();
form3.ShowDialog();
}
Then, after this CLR I run the event Application_Idle on which I add a method that must focus on the textBox2 control..
private void Application_Idle(object sender, EventArgs e)
{
form2.textBox2.Focus();
form2.textBox2.Select();
form2.textBox2.Focus();
Application.Idle -= new EventHandler(Application_Idle);
}
But when I click the button on form1, I see Form2 showing, Form3 showing and then Application_Idle method raise, but form2.textBox2 control doesn't get focused...
If I comment out the form3.ShowDialog(); line it's works fine, but how do I focus a form element with another form activation?(form3.ShowDialog()) ?
Remark added:
Problem in also is I have a strict architecture and all I can change is Application_Idle method.
The issue you are having is with modality:
Forms and dialog boxes are either modal or modeless. A modal form or dialog box must be closed or hidden before you can continue working with the rest of the application.
Dialog boxes that display important messages should always be modal. The About dialog box in Visual Studio is an example of a modal dialog box. MessageBox is a modal form you can use.
Modeless forms let you shift the focus between the form and another form without having to close the initial form. The user can continue to work elsewhere in any application while the form is displayed.
When you use ShowDialog, the form that is shown prevents the caller from returning control until the dialog box is closed. If this is not the desired effect, you can use the Show method.
You could focus the textfeld, when the form itself got the focus:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
this.GotFocus += (s, e) =>
{
this.textBox2.Focus();
};
}
}
As John Koerner stated, you cannot set focus to Form 2 while Form 3 is open because of modality.
Since you stated that a user input in Form 3 is necessary to proceed, you should change your approach. You can place a listener watch for Form 3's closing. Only then can you set the focus somewhere else
form3.FormClosed += Application_Idle