When bad client logic allows invalid data to make it to the database, only to be rejected on an insert or update operation, extra database calls are generated. This isn’t a problem when you’re developing applications for a small number of users in a connected environment. But in a disconnected, multitier environment with a large number of users, these unnecessary calls are very expensive in terms of resource utilization and scalability. Good application design must include the creation and reuse of client controls that minimize the possibility of invalid data being passed out of the presentation tier.
Microsoft provides a robust set of validation controls for ASP.NET programmers, but Windows Forms developers must implement their own controls. Although the natural inclination may be to minimize any text entry by using controls, it’s easy to create really cumbersome interfaces by adding too many controls. For example, using spinner controls to enter quantities or a date control as the only way to enter a date will result in an unwieldy interface. Ultimately, your users need the ability to enter text into some kind of TextBox control. This is one area where the value of inheritance within the .NET Framework becomes obvious.
By using the base TextBox or RichTextBox controls to create subclassed data entry controls, you greatly increase your control over the data entered by your users. In this article, I’ll present three ways to use inheritance and the capabilities of the .NET Framework to ensure that data entry errors are trapped in the user interface layer.
The validating TextBox control
One of the simplest but most elegant controls you can create is the validating TextBox control. This control has two main functions. First, it checks each keystroke as it’s entered to make sure it’s valid based on the field type. For example, a field expecting integers would verify that only the digits 1 through 9 are entered, throwing out other keystrokes automatically, whereas a field that allows a decimal value would also allow the comma and a single period.
The second function of a validating text box is to provide complex formatting and visual cues indicating that the feedback is valid. For example, you can add complex formatting, such as automatically inserting commas for a numeric when the field loses focus to make the display more visually appealing, and then automatically removing those commas when editing or when saving the numeric value to an object or business entity before passing it to the business tier for processing.
A validating text box created by inheriting from the base TextBox control has two compelling features. First, you can still use all of the underlying TextBox functionality to make your control even more useful. For example, if your user interface has access to a typed DataSet to store the instance data, and it’s been configured with a MaxLength value on the entry field, then you can set the Length property of the TextBox control equal to the MaxLength value of the entry field for additional validation. More importantly, since you’re checking entry on every keystroke by interrogating the key value in the TextChanged event, the control won’t have to resort to an exception to trap an invalid character.
For complex validation scenarios, it makes more sense to check the value of the control after all of the characters have been entered and the control loses focus. The .NET Framework includes a complete implementation of regular expressions syntax (RegEx) that allows you to perform many string functions, including a simple comparison of a string to a predefined string template. Oddly enough, ASP.NET validation controls provide this functionality, but Windows controls don’t. You can create your own RegEx control by inheriting from the base TextBox control and adding a RegEx expression property to the control.
The TextBoxValidating event fires when the control loses focus. You can code this event to determine whether the text entered by the user conforms to the expression entered in the new RegEx property by using code like this:
If not RegExProperty.IsMatch(Me.Text) then . . .
This code calls the IsMatch method of a RegEx object to determine if the text entered matches the mask you set for the RegEx object. If it doesn’t, you must display the error in an ErrorProvider control, pop up a message box, or give some other visual cue as to the source of the error. RegEx expressions can be used to verify everything from phone numbers and ZIP codes to Internet addresses and the custom field types that your data model expects.
The formatted stream control
The need to enter a formatted stream is a third common scenario for text entry. The standard RichTextBox control provides the ability to enter formatted text. Like its little brother the TextBox, the RichTextBox also inherits from the System.Windows.Forms.TextBoxBase class.
The ability to enter formatted text allows word processor-like data entry and formatting, as well as the ability to implement functions like keyword highlighting. You can use keyword highlighting within the RichTextBox to ensure that the correct terms are being used. Also, you can validate the contents of the entire RichTextBox to make sure it honors a predefined set of formatting rules, much like a lightweight XML parser. Any time you need to display or control formatting in a way that requires special text attributes like color, italics, or size, you can inherit from the RichTextBox to create a control that does what you need.
Finally, keep in mind that just because the data is formatted properly and contains only valid characters doesn’t mean that it’s valid data for your business rules or your data layer. By combining the techniques discussed here with other .NET features like typed datasets, you can greatly reduce the possibility that invalid data will be submitted either to the business layer for business rules evaluation or to the database in the data access layer.