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
.