Data Validation


You should never trust your users. I don't mean you can't trust them to pay (a separate issue that I won't go into here); I mean you can't trust the data that your users enter. They may not give you all the data that you need, or they may not give you data in the correct format. List boxes, radio buttons , and all the other controls that give users choices do so to make sure that they provide data in the correct format. However, sometimes you need to validate free-form data entry, such as what a user types into a text box. For that, you handle a control's Validating event:

  void applicantNameTextBox_Validating(object sender, CancelEventArgs e) {  // Check for existence of application name   if( applicantNameTextBox.Text.Length == 0 ) {     MessageBox.Show("Please enter a name", "Error");  e.Cancel = true;  }  }  

The Validating event is called when the focus is moved from one control on the form that has the CausesValidation property set to true to another control that has the CausesValidation property set to true ”for example, from a TextBox control to the OK button. The Validating event gives the handler the chance to cancel the moving of the focus by setting the CancelEventArgs.Cancel property to true. In this example, if the user doesn't enter a name into the text box, then the Validating event handler notifies the user of the transgression and cancels the event, keeping the focus on the text box containing invalid data.

If the Validating event is not canceled , the form will be notified via the Validated event:

  void applicantNameTextBox_Validated(object sender, EventArgs e) {  MessageBox.Show("Nice name, " + applicantNameTextBox.Text, "Thanks!");  }  

Each control has CausesValidation set to true by default. To allow the user to cancel the form without entering valid data, you must set the CausesValidation property to false for your Cancel or Close button:

 void InitializeComponent() {   ...  this.cancelButton.CausesValidation = false;  ... } 

Regular Expressions and Validation

One handy tool for data validation that's not specific to WinForms but is provided by .NET is the Regex class from the System.Text.RegularExpressions namespace. The Regex class provides a regular expression interpreter. A regular expression is a general-purpose way to describe the format of string data so that, among other things, a string can be checked to make sure that it fits a required format.

The regular expression language is beyond the scope of this book, [5] but let's look at an example. A string to check that the format of a phone number fits the U.S. format, including area code, parentheses, spaces, and dashes, would look like the following:

[5] For an overview of regular expression in .NET, read "Regular Expressions in .NET," by Michael Weinhardt and Chris Sells, Windows Developer , 11/02, http://www.wd-mag.com/documents/s=7547/win0212d/

 ^\(\d{3}\) \d{3}-\d{4}$ 

This regular expression breaks down as follows :

  • The leading "^" means to start checking the string from the beginning. Without this, any leading characters that don't match the regular expression will be ignored, something that could lead to improperly formatted phone numbers .

  • The "\(" means to match against a literal "(" character. The "\" prefix is necessary to escape the "(", which otherwise would be treated specially by the Regex class.

  • The "\d{3}" means to match three digits.

  • The "\) " means to match a ")" character, followed by a space character.

  • The "\d{3}-\d{4}" means to match three more digits, followed by a "-" character, followed by four more digits.

  • The trailing "$" means to match the string all the way to the end so that no other characters can come after the phone number.

This regular expression can be used in a Validating event handler to check for a U.S. phone number:

 void applicantPhoneNoTextBox_Validating(   object sender, CancelEventArgs e) {  Regex re = new Regex(@"^\(\d{3}\) \d{3}-\d{4}$");   if( !re.IsMatch(applicantPhoneNoTextBox.Text) ) {  MessageBox.Show(       "Please enter a US phone number: (xxx) xxx-xxxx",       "Error");     e.Cancel = true;  }  } 

If the string entered into the phone number text box does not match the regular expression in its entirety, the IsMatch method of the Regex class will return false, letting the handler indicate to the user that the data is not in the correct format. Taken together, regular expressions and validation provide a powerful tool to check a wide range of input strings provided by the user.

Data Format Notification

As much as I lean on the message box in my test development, I prefer not to use it for actual applications. For one thing, users are likely to forget the acceptable format for a phone number after the message box goes away. One alternative is to use a status bar, but status bars tend to be ignored because they're at the bottom of the screen, far away from what the user is looking at. [6] A better way is to use the ErrorProvider component, which shows the user that there's a problem and provides a tooltip with extra information, as shown in Figure 3.5.

[6] According to legend, Microsoft did a usability study awarding people $50 if they would look under their chair , putting the notification for this award in the status bar. The $50 went unclaimed during the testing.

Figure 3.5. Sample Use of the ErrorProvider Component

When the user attempts to change focus from the empty text box, we use an instance of the ErrorProvider component to set an error associated with that text box, causing the icon to be displayed to the right of the text box and making the tooltip available for more information. To implement this behavior, you drag an ErrorProvider component onto the form and handle the Validating event:

 void applicantNameTextBox_Validating(object sender, CancelEventArgs e) {  string error = null;  if( applicantNameTextBox.Text.Length == 0 ) {  error = "Please enter a name";  e.Cancel = true;   }  errorProvider1.SetError((Control)sender, error);  } 

Notice the call to ErrorProvider.SetError. The first argument is the control the error is associated with, which we get from the sender argument to the Validating event. The second argument is the error string, which is used as the tooltip. If there is no problem, the error is null, which causes the error indicator for that control to go away, showing that there's no error.

Thorough Validation

As useful as the Validating event is, especially when combined with the ErrorProvider component, there is one validation issue you'll have to deal with separately. Because the Validating event is triggered only when focus is moved from one control to another, if a control has invalid data but never receives focus, it will never be validated. For example, the form in Figure 3.5 has three text boxes. Even if you were to handle the Validating event for all three text boxes, the user could still enter valid data into the first one ( assuming it gets focus first) and press the OK button, causing the form to close and return DialogResult.OK. The problem is that the other two text boxes will never get focus, will never receive the Validating event, and therefore will never get a chance to cancel the acceptance of the form.

One way to deal with this problem is to make sure that all controls start with valid data, requiring the user to set focus to invalidate the data and triggering the validation events when focus is lost. However, there are lots of cases when you can't fill in valid initial data. What's a good default for a phone number? Or e-mail address? Or mother's maiden name? For these cases, you'll need to write the code manually to validate the form in the OK button's Click event handler:

 void okButton_Click(object sender, EventArgs e) {   // Validate each direct child control manually   foreach( Control control in this.Controls() ) {     control.Focus();     if( !this.Validate() ) {       this.DialogResult = DialogResult.None;       break;     }   } } 

This code walks the list of controls in the form's control collection, setting each one in turn to have the focus. By itself, this will not trigger the Validating event, unfortunately , so we also must to trigger that event by calling the form's Validate method. The Form.Validate method does not validate all the controls in the form. Instead, it validates only the control that just lost focus. If validation fails on any one control, we change the form's DialogResult property (preset to DialogResult.OK by the OK button's DialogResult property), and that stops the form from closing.

If you've got controls that contain other controls, such as controls contained by group boxes, you must be a little more thorough about checking each control for child controls to make sure that they're validated, too:

  // Retrieve all controls and all child controls etc.   // Make sure to send controls back at lowest depth first   // so that most child controls are checked for things before   // container controls, e.g., a TextBox is checked before a   // GroupBox control   Control[] GetAllControls() {   ArrayList allControls = new ArrayList();   Queue queue = new Queue();   queue.Enqueue(this.Controls);   while( queue.Count > 0 ) {   Control.ControlCollection   controls = (Control.ControlCollection)queue.Dequeue();   if( controls == null  controls.Count == 0 ) continue;   foreach( Control control in controls ) {   allControls.Add(control);   queue.Enqueue(control.Controls);   }   }   return (Control[])allControls.ToArray(typeof(Control));   }  void okButton_Click(object sender, EventArgs e) {   // Validate each control manually  foreach( Control control in GetAllControls() ) {  // Validate this control     control.Focus();     if( !this.Validate() ) {       this.DialogResult = DialogResult.None;       break;     }  }  } 

The GetAllControls method gets all of a form's controls, as well as all the controls' child controls, all those controls' children, and so on. This function is handy whenever you need to dig through the complete set of controls on a form, not just when you're validating. You'll see it used a few more times in this chapter.

The OK or Apply button's Click event handler is also a good place to do any other kind of validation that can't be done using the validation events, such as checking multiple related fields at the same time. If for any reason you don't want the form to close, set the form's DialogResult to None, and it won't.



Windows Forms Programming in C#
Windows Forms Programming in C#
ISBN: 0321116208
EAN: 2147483647
Year: 2003
Pages: 136
Authors: Chris Sells

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net