RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Exploring Secrets of Windows Form Validation  : Page 4

If you're tired of writing custom validation code for every input field in your applications—and of fixing the resulting validation errors—then you'll welcome the generic validation engine described in this article, which can automate many validation scenarios.

An Implementation
The validation engine I created for this article is called Validator. You can download it from my open-source web site (look under Products >> Documentation >> C# >> CleanCode.Forms.Validator). This engine validates form controls (TextBox or DataGridView Controls) using attributes you specify in the Tag property as described earlier. When errors occur, it displays both an individual error indication for each invalid entry, and can disable a designated common form submittal button (typically an OK button). You can display validation errors in either individual Label controls unique to each control, or with a standard ErrorProvider component, at your discretion. Using an ErrorProvider carries the advantage that you do not have to create and associate a Label component with each validating component, but the disadvantage that you must use the mouse to see the validation error, as discussed earlier. You can also download the sample application described in this article, which illustrates how to use the Validator.

To validate a control, you add it to a Validator instance, then call the Validate() method inside either the TextChanged or Validating event handler for your control. MSDN documentation suggests the Validating event handler is the appropriate place, but Validating fires only when the focus leaves a control. If you need finer control (such as on every keystroke) then TextChanged is a better choice.

The validation engine evaluates all validation attributes (business rules) for a given control, but reports only one error per control at a time. For example, if a single entry fails both the maximum length test and the maximum value test, the engine reports only one of those failures to the user. After the user corrects that error, then the remaining error condition will be displayed, if still present.

Coding the Example
Here's how to put all this into practice. For the simple example form given earlier, here is the user code (as distinct from the designer generated code). Each short method shown is an event handler, so it needs to be connected to an appropriate event (which should be evident from the method naming).

   using System;
   using System.ComponentModel;
   using System.Windows.Forms;
   using CleanCode.Forms;
   namespace ValidationDemo
      public partial class Form1 : Form
         private Validator validator;
         public Form1()
         private void Form1_Load(object sender, EventArgs e)
            validator = new Validator(okButton, errorProvider);
         private void okButton_Click(object sender, EventArgs e)
            MessageBox.Show("OK clicked");
         private void cancelButton_Click(object sender, EventArgs e)
            MessageBox.Show("cancel clicked");
         private void leftTextBox_Validating(object sender, CancelEventArgs e)
            if (!validator.Validate(leftTextBox)) { e.Cancel = true; }
         private void rightTextBox_TextChanged(object sender, EventArgs e)
         private void dataGridView_CellValidating(
            object sender, DataGridViewCellValidatingEventArgs e)
            if (!validator.Validate((DataGridView)sender, e))
               // e.Cancel = true; // this really traps the user in there!
That's all the validation code need for the sample application (see Figure 1) within Visual Studio 2005. The Validator contains the actual validation code. The only other entries you need to make are the business rules, which I'll cover shortly.

In the preceding code, the Form1_Load event handler instantiates the validation engine, then adds each control to validate to it. Note that the Validator constructor accepts the form's OK button. The Validator uses that reference to enable the OK button when all Controls validate and disable it (or "grey it out") if any controls do not validate. The constructor also accepts an ErrorProvider, which the validation engine uses to display errors.

The leftTextBox and rightTextBox controls use different event handlers merely to show the behavior differences discussed earlier—the former validates when focus leaves the field; the latter validates at every keystroke. The leftTextBox handler also prevents users from leaving as long as an error is present. You could, in fact, use the same event handler method for many controls if you want them all to act on the same event and behave the same way.

For the DataGridView control—a control containing multiple data values—calling the method to perform validation is no more complicated than a singleton control, but the business rule set requires more information as you'll see next. The possibilities involved in handling DataGridView validation are much more sophisticated, particularly with bound DataGridViews, but those are beyond the scope of this article.

Business Rules for the Example
In addition to the code discussed earlier, you must instrument the leftTextBox, rightTextBox, and dataGridView controls with business rules. Specifically, you set the Tag property of these controls as shown in Table 3:

Table 3. Sample Tag Property Values: The table lists the Tag settings for the three TextBox controls in the sample application.
Control Tag property
leftTextBox pattern=^\w[\w\.]*\@\w+\.\w[\w\.]*$;minLen=2
rightTextBox pattern=^-?(?:\d*\.?\d+|\d+\.)$
dataGridView [Column]pattern=^\w+$;[MatchExpr]pattern=\(.*\)

These rules state—in the language of regular expressions—that the leftTextBox must have an entry resembling an email address; "foo@bar.com" will pass muster, as will "e@a.b.c.d". The rightTextBox business rule requires numeric input, for example, 5 or -25 or -3.9999 or .81. The dataGridView may have business rules for each column—but does not have to. In this case, the rules specify that the column generically called "Column" must contain only letters or digits and the column called "MatchExpr" must contain an opening and a closing set of parentheses somewhere in the input.

Author's Note: In Table 3, notice the format used to embed multiple sets of business rules into one property—each column name is in brackets followed by its business rule set.

Notes on Running the Example
Build the application and execute it to see what happens. The button will be disabled if any validation rule fails. In this case, the Validator uses an ErrorProvider to display validation errors. Because your code creates the ErrorProvider independently, you are free to add further validation checks in your event handlers that are beyond the scope of the business rule categories provided by the validation engine.

At this point, I hope you understand both how and why to separate business rules from code, using a validation engine to help do that. I encourage you to study the source code to understand the mechanisms of the validation engine itself. This is actually a simplified version of an earlier validation engine called "PageValidator," implemented in Perl and JavaScript for use on a web site. The same engine runs on both the client and the server, providing for both rapid response and security.

Michael Sorens is a freelance software engineer, spreading the seeds of good design wherever possible, including through his open-source web site, teaching (University of Phoenix plus community colleges), and writing (contributed to two books plus various articles). With BS and MS degrees in computer science and engineering from Case Western Reserve University, he has worked at Fortune 500 firms and at startups, using C#, SQL, XML, XSL, Java, Perl, C, Lisp, PostScript, and others. His favorite project: designing and implementing the world's smallest word processor, where the medium was silicon, the printer "head" was a laser, and the Declaration of Independence could literally fit on the head of a pin. You can discuss this or any other article by Michael Sorens here.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date