Implementing a Custom Windows Control

As mentioned earlier, components do not have a visual aspect at runtime; controls do. You can create a custom control by defining a new control that inherits from System.Windows.Forms.Control or an existing control, like the TextBox control. If you want to create a brand new control, inherit from the Control class. Intuitively it will be easier to create a control if you inherit from one that already has much of the functionality you need.

Defining a Regular Expression TextBox Control

You may use a Validating event to write code to validate the value of a TextBox control. Using an event handler is convenient for general purpose validation; however, if you find yourself using a specific kind of validation repeatedly, you can save time by incorporating that validation into a custom TextBox control.

Regular expressions represent a powerful means of performing pattern matching of a complex nature. The RegexTextBox control incorporates the power of regular expressions with TextBox validation.

Listing 9.3 defines a custom TextBox control that has a Pattern property. The Pattern property represents a regular expression. Assigning a regular expression string to RegexTextBox will validate the input against the regular expression. This control is a great building block for specifically typed TextBox controls. (The source code for RegexTextBox is contained in RegexTextBox.sln.)

By convention in .NET, methods raise events, and those methods have an On prefix. For example, a Validating event is raised by an OnValidating method. Thus, if you generalize a control with the Validating event and you want to extend the validation behavior, you will override the OnValidating event. Listing 9.3 demonstrates how to add a new property to a custom control and override a behavioral method.

Listing 9.3. Implementing the RegexTextBox Control
1:  Imports System.ComponentModel
2:  Imports System.Text.RegularExpressions
3:
4:  Public Class RegexTextBox
5:    Inherits TextBox
6:
7:    Private FPattern As String
8:
9:    Public Property Pattern() As String
10:   Get
11:     Return FPattern
12:   End Get
13:   Set(ByVal Value As String)
14:     FPattern = Value
15:   End Set
16:   End Property
17:
18:   Protected Overrides Sub OnValidating( _
19:     ByVal e As CancelEventArgs)
20:     If (Regex.IsMatch(Text, FPattern) Or _
21:       FPattern = String.Empty) Then
22:       e.Cancel = False
23:       MyBase.OnValidating(e)
24:     Else
25:       DoValidatingError(e)
26:     End If
27:   End Sub
28:
29:   Private Sub DoValidatingError(ByVal e As CancelEventArgs)
30:     Const Message As String = _
31:       "Text does not match pattern expression {0}"
32:
33:     If (MessageBox.Show(String.Format(Message, FPattern), _
34:       "Properties Window", MessageBoxButtons.OKCancel, _
35:       MessageBoxIcon.Exclamation) = DialogResult.OK) Then
36:       e.Cancel = False
37:       MyBase.OnValidating(e)
38:     Else
39:       e.Cancel = True
40:       MyBase.ResetText()
41:     End If
42:
43:   End Sub
44:
45: End Class

The private field FPattern holds the regular expression, and the public property Pattern is used to get and set the value of the underlying field. The OnValidating method overrides the inherited OnValidating method (see lines 18 through 27). Recall that we are overriding the behavior of a method that raises an event. To make sure that the original behavior happens we need to invoke the inherited method. I elected to invoke the inherited method only on the condition that my custom validation occurs. As a general rule, always invoke the base behavior. However, you can reserve the right to break from conventional wisdom as long as you are aware of possible adverse consequences (see Note below).

Finally, lines 29 through 43 enact some simple gyrations to let the RegexTextBox user decide whether to continue with an invalid value in the TextBox control if the input value does not match the regular expression pattern.

NOTE

As a rule, programmers using a class should concern themselves not with how a class is implemented but with how a class works. After all, this is the reason we have access modifiers like Public, Private, and Protected. Users should worry about only public details. Alternatively, generalizers—that is, people who inherit from and extend existing classes—must concern themselves with the implementation details of public and protected members. Thus, Microsoft should release the source code to the base class libraries (BCLs), including the source code for ADO.NET and controls like TextBox. Releasing Rotor is a good first step, but Microsoft should follow Borland's lead and provide us with the source code for controls too. Clearly Borland suffered no deleterious effects for releasing its Visual Control Library (VCL); in fact, Borland reaped the benefit of a cottage industry of component builders. Borland's problems have never been related to its technology.

Having said all that, it is possible that conditionally calling the inherited OnValidating method may result in adverse side effects. Without the source code to TextBox it may be impossible to know for sure. If you experience problems with the conditional behavior of RegexTextBox, let me know at [email protected].


Testing the Custom Component

Testing custom controls before inserting them into the Toolbox is as easy as declaring and creating an instance of the control. Listing 9.4 demonstrates how we might test the RegexTextBox control before incorporation into the Toolbox and general dissemination. Because the TextBox control has a Windows handle—hWnd—and needs to receive messages like WM_PAINT, it is essential that we add the programmatically created RegexTextBox to the form's Controls collection.

Listing 9.4. Declaring and Creating an Instance of RegexTextBox for Testing
1:  Public Class Form1
2:      Inherits System.Windows.Forms.Form
3:
4:  [ Windows Form Designer generated code ]
5:
6:    Private RegexTextBox1 As RegexTextBox
7:
8:    Private Sub Form1_Load(ByVal sender As System.Object, _
9:      ByVal e As System.EventArgs) Handles MyBase.Load
10:
11:     RegexTextBox1 = New RegexTextBox()
12:     RegexTextBox1.Pattern = "^d+$"
13:     Controls.Add(RegexTextBox1)
14:
15:   End Sub
16: End Class

Line 13 demonstrates the step of adding the RegexTextBox1 instance to the form's Controls collection. Line 12 shows a regular expression that matches a string containing one or more digits. The caret (^) and dollar sign ($) indicate expression boundaries. The result is that the text must be a contiguous string of digits with no preceding white space. If the string does not match, a message box appears to alert the user (Figure 9.1).

Figure 9.1. The RegexTextBox control will display a message box similar to the one shown if the pattern does not match the input text.


Regular expressions represent an entire language themselves. The regular expression language in .NET is designed to be compatible with Perl 5. Regular Expressions with .NET by Dan Appleman [2002] is an excellent 75 page e-book on .NET regular expressions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.70.0