After searching through many lists for .NET, I have realized that
CausesValidation has some issues, before .NET 2.0.
Note: With the introduction of .NET 2.0, these issues can be avoided by using
AutoValidate and changing it to AutoValidate.EnableAllowFocusChange. When the
OK button is clicked, call Validate to fire all the validating event handlers
for the form; call ValidateChildren if the form has container controls
that have controls with validating events.
Issue 1 According to the .NET documentation, if a button has the
CausesValidation property set to false then validation
events should not fire. This is not correct if a form is opened using
Show instead of ShowDialog. In this case, the button's
event handler is called first, then when the handler calls the
Close method, the validation events fire.
Solution In this case it is possible to fix the error because after
the validation events fire, the Closing event fires. The closing
event receives the same EventArgs as the validating events,
so the solution is to set e.Cancel = false;
private void LoanApplicationDialog_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = false;
}
However, this only works in Microsoft .NET Framework 1.1. In version 1.0, you will need to
use the alternate solutions listed below.
Issue 2 According to the .NET documentation, if a button has its
DialogResult set to Cancel and the form has the
CancelButton set to this button, then when the user presses
the ESC key, the button's event handler should fire. If there is a control
that has a validating event and the control has invalid data, then the validating
event handler will be called before the cancel button's event handler is called
and will prevent the form from closing. Apparently, the validation event
fires twice if the form itself has CausesValidation set to
true. However, even with CausesValidation set to
false for the form, there is still a mysterious call to the validating
event handler that cannot be prevented by setting/unsetting a property.
Solution In this case, it is necessary to override the
ProcessDialogKey and intercept the ESC key and call the event
handler for the button directly.
protected override System.Boolean ProcessDialogKey ( System.Windows.Forms.Keys keyData )
{
if (keyData == Keys.Escape)
{
//call the button click event directly
this.cancelButton_Click(null, new EventArgs());
//return true to indicate that the key has been handled
return true;
}
else
{
return base.ProcessDialogKey(keyData);
}
}
Issue 3 The previous solutions should handle most cases. Issue
2 deals with the ESC key, but the same thing happens if the Close button
(the X) on the control bar is clicked. However, in this case, the
Closing event will be fired, so the first solution will also
fix that problem. However, if more control is needed, the following code
can be used to intercept the key press of the Close on the control bar. In
the section for the SC_CLOSE message, a method could be called, or a boolean
flag could be set to indicate that the form is closing. There is a choice
to be made concerning the call to the base class WndProc. Do
not call it if you plan to handle the event entirely yourself.
public enum WindowMessages
{
WM_SYSCOMMAND = 0x0112,
SC_CLOSE = 0xf060
}
protected override void WndProc ( ref Message msg )
{
if ( msg.Msg == (int)WindowMessages.WM_SYSCOMMAND )
{
switch ( msg.WParam.ToInt32() )
{
case (int)WindowMessages.SC_CLOSE:
{
//map to a button, or set a flag
//to identify that the form is closing
}
break;
}
}
// Call base class function
base.WndProc(ref msg);
}
Alternate Solution The alternate solution is to fire the validation
events under program control instead of using the Windows default event.
In this case, turn off all CausesValidation properties and define
a new delegate.
public delegate void ValidateControl(object sender,
System.ComponentModel.CancelEventArgs e);
+= and -=, assignment
is not allowed.public event ValidateControl validateControl;
Validating event,
then remove the validating event, but keep the method definition. You can
also use Visual Studio to auto-generate the ValidateControl
methods.
+= must be used to associate an object with the delegate. Think
of this as a linked list and that each new object is added to the end of
the previous delegate in the list.validateControl += new
ValidateControl(applicantNameTextBox_Validating);
validateControl += new
ValidateControl(applicantLoanAmountTextBox_Validating);
args.Cancel, breaking if it is true. This is the
technique that is used by the default CausesValidation process.
foreach( ValidateControl vc in validateControl.GetInvocationList() )
{
CancelEventArgs args = new CancelEventArgs();
vc(sender, args);
if (e.Cancel) break;
}
//test args.Cancel to see if there was success or failure
CancelEventArgs that you send to the delegate. The same arguments
will be passed in turn to each handler.
CancelEventArgs args = new CancelEventArgs(); if (validateControl != null) validateControl(sender, args); //test args.Cancel to see if there was success or failure
WndProc.