How to prevent keystroke in child form reaching parent form? - c#

I have a form that, upon user request, opens a child form, like so --
private void toolTrim_Click(object sender, EventArgs e)
{
Form form = new TrimOptions();
form.ShowDialog();
if (form.DialogResult == DialogResult.OK)
{
//code;
}
{
This child form has a button called btnOk and its property AcceptButton set to btnOk. This means that, when the Enter key is pressed in the child form, it's as if you had clicked on the Ok button, and this code executes.
private void btnOk_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Save();
DialogResult = DialogResult.OK;
Close();
}
The problem is that, when the child form closes, the Enter key that was used to close the child form is captured by the DataGridView in the parent form.
private void filesDataGridView_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Return) //execution stops here if breakpointed
There's a legitimate use for that, but only when the DGV has focus, not when some child form of this form has focus.
So, how to prevent keystrokes in a child form from "bubbling up" to a parent form?

Following Steve's advice, I removed the Close(); having learned that setting DialogResult to any value other than None will close the window.
Following Hans Passant's advice to use KeyDown instead of KeyUp, I realized I had a mixture of KeyDowns and KeyUps. He was also correct, of course, that indeed the DGV had focus. I made them all KeyDowns, and my problem went away.

the other solution would be before opening modal you can disable the parent form once dialog is closed you can enable the parent form, so key up event does not fire.

Related

How to Override standard close (X) button in a Windows Form

How do I go about changing what happens when a user clicks the close (red X) button in a Windows Forms application (in C#)? I want to add a simple DialogResult and Messagebox to ask user if he's sure about closing the form.
You should handle the FormClosing event on the Form class. In the handler, you are able to set the value of the CancelEventArgs.Cancel property to true to prevent closure of the form.
Example:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("Close?", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
e.Cancel = true;
}
You can create the handler method by double-clicking the event in the Properties tab while the Windows Forms designer is open:
See https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.formclosing?view=windowsdesktop-6.0

Why isn't the Validated event firing for controls on a form created in the main form when the child form is closed?

Say I have a winforms application with two forms, the main form that starts up when the program runs, and another form. Here is the code for the main form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var f2 = new Form2();
f2.ShowDialog();
}
private void textBox1_Validated(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Main Form: Validated!");
}
}
And this is the child form:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void textBox1_Validated(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Child Form: Validated!");
}
}
When I run the application, I can put the focus in the textbox on the main form, and of course when I tab out, it fires the Validated event and prints Main Form: Validated! to the output. This also happens if I have the focus in the textbox and close the main form (i.e. end the program).
When I click the button on the main form which pops up an instance of the child form, I can put the focus in the textbox on the child form, and the Validated event fires as it should when I tab out of it. However, unlike the main form behavior on closing the form, if I have the focus in the textbox on the child form and I close the child form, the Validated event never fires.
Why doesn't the validated event fire, and is there a way I can make it fire.
I'm relying on the validated event of certain controls to update my view models. I want to make sure they always fire even when the loss of focus is due to a form closing or even the application itself ending.
This is caused by ShowDialog(). It is a documented bug, a mistake in .NET 1.x that they could not fix anymore. From the Form.cs source code:
// NOTE: We should also check !Validate(true) below too in the modal case,
// but we cannot, because we didn't to this in Everett (bug), and doing so
// now would introduce a breaking change. User can always validate in the
// FormClosing event if they really need to. :-(
So just follow the guidance:
protected override void OnFormClosing(FormClosingEventArgs e) {
if (e.CloseReason == CloseReason.UserClosing && this.DialogResult != DialogResult.Cancel) {
if (!base.Validate(true)) e.Cancel = true;
}
base.OnFormClosing(e);
}
With the assumptions that you don't need the event when the dialog is dismissed and you'd want the dialog to stay open when validation fails. The latter clause is new behavior.
Documentation says:
Unlike non-modal forms, the Close method is not called by the .NET
Framework when the user clicks the close form button of a dialog box
or sets the value of the DialogResult property. Instead the form is
hidden and can be shown again without creating a new instance of the
dialog box.
Based on the docs, when you call the ShowDialog method of the second form (Form2) and you close it by clicking on the close button (the button with an X at the upper-right corner of the form) the form is hidden (not closed). This is why the Validated event never fires.
Note: Closing and Closed events will fire anyway.
Note2: Closing the form using ALT + F4 is the same as clicking on the X button of the form.
Demonstration
Form as modal (your example)
The second form is opened by calling ShowDialog.
Add a button on the second form and set the click event as follow:
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
If you close the form by clicking in that new button the Validated event will fire. If you close the form by clicking the button with an X at the upper-right corner of the form, the Validated event won't fire (because the form has never closed, it has been hidden).
Form as non-modal
The second form is opened by calling Show.
In this case, when you click the button with an X at the upper-right corner of the form the latter will be really closed (not hidden) and the Validated event fires.
Workaround
Hiding the ControlBox
The simplest method is to hide the X button of the form setting the ControlBox property to false (by designer or by code). However, the user could close the form using ALT + F4.
Using Hans Passant solution
Hope to be clear.
If you want, all your modal dialog controls validated, while closing .. probably you can approach below way.... which definitely fires the text box validated event.
Add an event for form closing in Form2 and call ValidateChildren()
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
this.ValidateChildren();
}
MSN Documentation
According to MSN Documentation If the CausesValidation property is set to false, the Validating and Validated events are suppressed.
Verifies the value of the control losing focus by causing the Validating and Validated events to occur :According to MSN Documentation
You can also fired Validating and Validated events by using Validate() method that lose the focus of controls. So you can call Validate method within closing event of Form2. Bellow is the code
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void textBox1_Validated(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Child Form: Validated!");
}
private void Form2_Closing(object sender, FormClosingEventArgs e)
{
if (!Validate())
//Write your code that will execute if your form is not validated
}
}

Must click "X" on MDIParent form multiple times to close application, each click closes MDIChild

I have started prototyping a C# MDI application and am running into an issue. It seems that when an MDIChild is open in the MDIParent I have to hit the close buttom on the parent multiple times to close the application. Each click on the close button closes one of the MDIChildren.
I suspected that it had to do with my MDIChildren's base form's on close method.
private void _AssetFormBase_FormClosing(object sender, FormClosingEventArgs e)
{
if(sender != this.MdiParent)
{
e.Cancel = true;
this.Hide();
}
}
Though my trick above does not seem to work. I assume that when the MDIParents close is called it in turn first calls all of its childrens close methods. So if the sender is the parents then instead of cancelling and hiding (to preserve the forms state), I would not do this and allow whatever normally happens to happen.
Any idea what the issue might be?
The sender is not what you think it is. Use e.CloseReason instead, you'll get CloseReason.MdiFormClosing. But don't test for that specific value, you also don't want to prevent the operating system from shutting down. Use:
private void _AssetFormBase_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Hide();
}
}
Note that you'll also get UserClosing when you call Close() in your own code.

FormClosing with CloseReason = UserClosing doesn't work expectedly?

I have a main form, in the class of this form, I declare another form. This form lives with the main form until the main form is unloaded. There is a button on the main form, clicking this button will show the member form (I mentioned above). I want to prevent the member form from closing when user closes that form and I added the following FormClosing event handler for that form:
private void MemberForm_FormClosing(object sender, FormClosingEventArgs e) {
if(e.CloseReason == CloseReason.UserClosing){
e.Cancel = true;
Hide();
}
}
That works OK for that form. However if user closes the main form, this form is not closed, and it's hidden somewhere making my application seem to run silently. I want this form also to be closed. This is very simple by adding some FormClosed event handler for my main form to close the member form manually. Closing it manually is OK, but why do I have to do that? It seems that when user closes the main form, the FormClosing event of the member form is fired with a parameter FormClosingEventArgs passed in and the CloseReason is the same as the CloseReason of the main form (which is UserClosing). I don't understand this, I thought the CloseReason of the form is UserClosing only when user clicks on the X button, I thought the CloseReason for my member form is something like "MainFormClosing".
Is there some way to close the member form automatically as by default?
UPDATE
Here is the method showing the member form (showing it as a dialog):
private void ShowMemberForm_Click(object sender, EventArgs e){
memberForm.ShowDialog();
}
But I don't think this matters, because when I load my main form, even I don't need to click on the ShowMemberForm button, and try closing my main form first, it doesn't still close the member form.
Thanks!
UPDATE
There is something strange here, I've tried commenting out the line e.Cancel = true, or even all the FormClosing event handler and the problem is still there. This is so strange, it works OK before, I've just added the member form and this form relates to some Thread handling, but the thread starts only when a button on the member form is clicked. I didn't click that button.
What I have done in the past is set a flag when programatically closing
so in the MemberForm
private bool _ForceClose = false;
public void ForceClose()
{
_ForceClose = true;
this.Close();
}
private void MemberForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(!_ForceClose)
{
e.Cancel = true;
Hide();
}
}
Then in your MainForm you can call
memberForm.ForceClose();
From within your MainForms FormClosing method or from your MainForms Dispose() or Deconstructor.
It's low tech, but it works. Im not sure if you should put _ForceClose = true in your MemberForm's Dispose method, i'm fairly certain when it gets there its already been closed BUT it couldn't really hurt.

Transition between two forms using show and hide does not behave as I expect

I have two forms. The first is the parent and contains a reference to the second and a button:
public class Form1:Form {
private Form2 frm2;
private Button btnShow;
...
}
When I press the button frm2 must be shown (visible and in front of all other windows) and Form1 must hide.
When I press the closebox of frm2, frm2 must hide and Form1 must be shown (reverse).
I used the click event of btnShow to register a handle that all it does is:
private void click(object sender, EventArgs e)
{
Hide();
frm2.Show(this);
}
and in the FormClosing event of frm2:
private void byebye(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
Owner.Show();
}
}
My codes works for 90% of the time but sometimes (i cannot specify when) the shown form
is not brought to front or loses focus (I am not sure about that i think the first).
What I am doing wrong. I've tried alternating the order of Show and Hide and using of a new reference instead of Owner, I also used Activate,BringToFront, Focus but with no success...
Why not simply:
{
Form2 fm = new Form2();
this.Visible = false;
fm.ShowDialog();
this.Visible = true;
}
Works for me.
Hide();
Owner.Show();
You are doing this the wrong way around. For a split second, there's no single window left in your application that can receive the focus. The Windows window manager is forced to find another window to give the focus to. Which will be the window of another application. With good odds that this is a large enough window to cover your own. Your Show() call will thus make your window visible again, but now underneath that window that got moved into the foreground. This doesn't always happen btw, the window manager appears to use a small timeout. The longer the owner has been hidden, the greater the odds that its code has been swapped out and that showing it takes more time, thus tripping the timeout.
The workaround is simple, just swap the two statements so you'll always have window that can be focused. Fix:
Owner.Show();
Hide();

Categories

Resources