http://www.developer.com/

Back to article

Creating a Custom Control and Custom UITypeEditor in VB - Part 1


January 23, 2003

Small, incremental changes to existing controls can be the best way to extend .NET. Small changes are easy to implement and test and can add real value. Incremental changes provide variety, and create an environment where you are layering new behavior on established controls.

In this part of a two-part article, I will be demonstrating how to marry the power of regular expressions to a WinForms TextBox. The regular expression will play the role of input validator. Rolling validation into the TextBox is an example of a small yet valid change. In Part 2 of this article, I will show you how to complement the Regular Expression TextBox by adding a custom type editor. The editor—a UITypeEditor—will permit you to verify at design time that the regular expression is syntactically correct.

Let's get started on the RegexTextBox.

Implementing a Custom Control

In keeping with our small, incremental change theme, I defined a custom TextBox that inherits from the existing System.Windows.Forms.TextBox control. The new TextBox is named RegexTextBox. I added an Expression property that is designed to contain a regular expression, and I provided an overridden implementation of the inherited OnValidating method. At runtime, when the TextBox.Text property is validated, my new method is called. The new method compares the Text property to the regular expression property (Expression), if one is provided. If the Text does not match the rule defined by the expression, the user is notified; that is, validation fails. Listing 1 contains the source code for the RegexTextBox control. (The class module is part of a class library. In my code I named the class library CustomControls, but you can name it anything you'd like.)

Listing 1: A custom TextBox that validates the Text property against a Regular Expression.

1:  Public Class RegexTextBox
2:    Inherits TextBox
3:
4:    Private FExpression As String
5:
6:    Public Property Expression() As String
7:    Get
8:      Return FExpression
9:    End Get
10:   Set(ByVal Value As String)
11:     FExpression = Value
12:   End Set
13:   End Property
14:
15:   Protected Overrides Sub OnValidating( _
16:     ByVal e As CancelEventArgs)
17:     If (FExpression = String.Empty OrElse _
18:       Regex.IsMatch(Text, FExpression) OrElse _
19:       AcceptMismatch()) Then
20:       MyBase.OnValidating(e)
21:     Else
22:       e.Cancel = True
23:     End If
24:   End Sub
25:
26:   Private Function AcceptMismatch() As Boolean
27:     Dim Message As String = String.Format( _
28:       "Text {0} does not match expression {1}", _
29:       Text, FExpression)
30:
31:     Return MessageBox.Show(Message, "Validating", _
32:       MessageBoxButtons.OKCancel, MessageBoxIcon.Error) = _
33:       DialogResult.OK
34:   End Function
35:
36: End Class

The Expression property returns and assigns the value of the associated field FExpression. Nothing too radical here, so it is easy to test. The work happens in the OnValidating method. OnValidating tests to determine whether the Expression is empty, the expression matches, or the user wants to accept a bad expression. If any of these conditions is true, the base—MyBase.OnValidating—method is called on line 20. If any of these conditions fail, Cancel is set to True on line 22 and navigation to the next control doesn't happen. The user has to resolve the invalid text or explicitly agree to accept it.

The OrElse condition is used to provide logic short-circuiting. For example, we don't want AcceptMismatch to be called—see lines 26 through 34—unless the Expression is not empty and the Regex.IsMatch test fails. If we use plain old Or, we get the AcceptMismatch dialog every time, which of course is a logic error. The extra, short-circuited OrElse operator was a compromise between Microsoft's desire to tighten up Boolean evaluations and a VB developer's not wanting modified logic behavior (although who these VB developers were is unknown).

Finally, AcceptMistach displays a message box that informs the user that the input Text failed validation. Clicking OK indicates that this is an acceptable state of affairs; clicking Cancel indicates that the user wants to try again.

Testing the Custom Control

Controls are just classes. We can create an instance of the RegexTextBox directly, provide an Expression value, and test the control before adding it to the Toolbox. (Although adding a control to the Toolbox is pretty easy in .NET, I like to give controls a quick dry run before installing them.)

To test the RegexTextBox, create a new Windows Forms application. Add a reference to the class library containing the RegexTextBox. The code in Listing 2 demonstrates an example of dynamically constructing and testing the RegexTextBox in a Windows Forms application.

Listing 2: Dynamically loading and testing the custom RegexTextBox control.

1:  Public Class Form1
2:    Inherits System.Windows.Forms.Form
3:
4:    [ Windows Form Designer generated code ]
5:    Private Sub Form1_Load(ByVal sender As System.Object, _
6:      ByVal e As System.EventArgs) Handles MyBase.Load
7:
8:      Dim Control As CustomControls.RegexTextBox = _
9:        New CustomControls.RegexTextBox()
10:     Control.Expression = "^\d+$"
11:     Controls.Add(Control)
12:     Control.Text = "w"
13:    
14:   End Sub
15: End Class

I added a reference to the CustomControls.dll. Line 8 declares a variable and line 9 creates an instance of the RegexTextBox. Line 10 adds a regular expression. This expression means that we are looking for a contiguous string of digits (\d+) with no preceding (^) or trailing ($) characters. Line 11 adds the RegexTextBox instance to the Form's Controls collection, which allows the control to be displayed and receive messages, and line 12 assigns the Text property some invalid text. Simply closing the form will cause the Validating behavior to occur. Because "w" is not a contiguous string of digits, the AcceptMismatch dialog is displayed. If you click OK the application closes; if you click Cancel then the application does not close.

Before releasing the control to the general public, you will want to devise some more positive and negative tests. For example, test to make sure the control works when a valid entry is assigned to Text and when the Expression is a null string. What happens if the Expression is not a valid regular expression?

Installing the Custom Control

Installing controls is straightforward. Assuming you added your custom control to a Class Library, you can install the control from the Toolbox. Right-click on the Toolbox and select Customize Toolbox. Using the Customize Toolbox dialog, pick the .NET Framework Components tab and browse to your control's DLL. After you pick the DLL and click OK, the control will show up in the Toolbox. You can also add a tab from the Toolbox's context menu for your custom controls.

To add additional fit and finish, you will probably want to provide a description for your new properties. (Refer to the DescriptionAttribute in the Visual Studio .NET help.) You may also want to provide a custom bitmap, instead of using the default Gear bitmap. Custom bitmaps can be associated with custom controls with the ToolboxBitmapAttribute. You can look up these features in the Visual Studio .NET help or pick up a copy of The Visual Basic .NET Developer's Book, which has a comprehensive section on building professional, custom components and controls.

Summary

Custom components and controls do not have to be complex to be valuable. By associating a regular expression with a TextBox, we can offer a tremendous amount of validation capability through the Regular Expression language in .NET. The additional code is relatively easy; the results are more robust and potentially bullet-proof user input. (All you have to do is provide the regular expressions.)

In the second half of this article (later this month), I will demonstrate how to create a custom type editor, a UITypeEditor. The UITypeEditor is used when custom control editing at design time is needed. For our purposes, we will create a UITypeEditor that facilitates experimentation with the regular expression language in .NET at design time. Stay tuned.

About the Author

Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. Look for his recent book "Advanced C# Programming" from McGraw-Hill/Osborne on Amazon.com. Paul Kimmel is available to help design and build your .NET solutions and can be contacted at pkimmel@softconcepts.com.

# # #

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date