The textbox we are about to discuss will have the regular asp.net validator controls built in and configurable as properties from
within the textbox. Also the textbox's input type is customizable and varies between Text,Digits,Alphabets...
The main reason for integrating validation with the textbox is the waste of time and the repeated extra work we need to do every time
we needed validation for the regular asp.net textbox. This article is part 1 of the series of articles Building The Ultimate Textbox. I recommend to check that article which contains a downloadable project of the textbox library with source code(in part 2)
There will be a second part for this article that will explain how to add more features to our textbox such as the
JQuery UI DateTime Picker control and the google like AutoSuggest functionality which uses a webservice also integrated within the
textbox. For this version of the article we are going to stick to validation and switching input modes.
- Supports several input modes
- Integrated validation
- Rich error message display
We will start by creating a new
Class Library project in visual studio then add a
reference to
System.Web namespace.
Create a new class that inherits from System.Web.UI.WebControls.TextBox. In order
to allow our control to be added and used from thr toolbox like the regular asp.net controls,
we need to add a ToolboxData at the class level.
Imports System.ComponentModel
Imports System.Web.UI
Imports System.Drawing
Imports System.Globalization
Imports System.Text
Imports System.Web.UI.WebControls
<ToolboxData("<{0}:MyTextBox runat="server"></{0}:MyTextBox>")> _
Public Class MyTextBox
Inherits System.Web.UI.WebControls.TextBox
End Class
To select which input mode the textbox will operate with, we need to add an an enum with all possible modes then create a property from that enum that can be configured from
visual studio Properties window.
Public Enum InputTypeMode As Byte
[Text] = 1
[Date] = 2
[DateTime] = 3
[Email] = 4
[Digits] = 5
[Decimal] = 6
[Alphabets] = 7
End Enum
<Browsable(True)> _
Public Property InputType() As InputTypeMode
Get
If ViewState("InputType") Is Nothing Then
ViewState("InputType") = InputTypeMode.Text
End If
Return ViewState("InputType")
End Get
Set(ByVal Value As InputTypeMode)
ViewState("InputType") = Value
End Set
End Property
Note the use of the Browsable attribute which tell visual studio that this property will be visible in the Properties window at design time.
The developer will be able to select which mode the textbox will run. If Text is selected, any string can be written, if digits is selected, only numbers from 0 to 9
are allowed and so on. So we need to write some javascript to handle user input. The javascript methods must be written within the textbox class and will be registered
while rendering the control specifically on the RenderBeginTag event.
Protected Overrides Sub AddAttributesToRender(ByVal writer As _
Web.UI.HtmlTextWriter)
MyBase.AddAttributesToRender(writer)
writer.AddAttribute(HtmlTextWriterAttribute.Name, Me.UniqueID)
writer.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID)
writer.AddAttribute(HtmlTextWriterAttribute.Type, "text")
writer.AddAttribute(HtmlTextWriterAttribute.Value, Text)
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, _
ColorTranslator.ToHtml(Color.DarkGray))
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid")
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "1px")
Select Case InputType
Case InputTypeMode.Digits
If Not Page.IsStartupScriptRegistered( _
"DigitKeyPressed") Then
Page.RegisterStartupScript("DigitKeyPressed", _
getJS4DigitKeyPress)
End If
writer.AddAttribute("onKeyPress", _
"return DigitKeyPressed();")
Case InputTypeMode.Alphabets
If Not Page.IsStartupScriptRegistered( _
"AlphabetKeyPressed") Then
Page.RegisterStartupScript("AlphabetKeyPressed", _
getJS4AlphabetKeyPress)
End If
writer.AddAttribute("onKeyPress", _
"return AlphabetKeyPressed();")
Case Else
If Not Page.IsStartupScriptRegistered( _
"KeyPressed") Then
Page.RegisterStartupScript("KeyPressed", _
getJS4KeyPress)
End If
writer.AddAttribute("onKeyPress", _
"return KeyPressed();")
End Select
End Sub
Overriding the AddAttributesToRender allows for adding attributed to be rendered for the textbox control that will be rendered as an html input. The javascript methods
called above are implemented below:
Private Function getJS4DigitKeyPress() As String
Dim js As String
js = "<script language="javascript">"
js &= "function DigitKeyPressed() "
js &= "{"
js &= " if( !(event.keyCode >= 48 && event.keyCode <= 57))"
js &= " {"
js &= " return false; "
js &= " }"
js &= " " & Me.UniqueID & "_KeyPressStatus=true;"
js &= "}"
js &= "</script>"
Return js
End Function
Private Function getJS4AlphabetKeyPress() As String
Dim js As String
js = "<script language="javascript">"
js &= "function AlphabetKeyPressed() "
js &= "{"
js &= " if ((event.keyCode >= 65 && event.keyCode " & _
"<= 90) || event.keyCode==32 || " & _
"(event.keyCode >= 97 && " & _
"event.keyCode <= 122))"
js &= " {"
js &= " return true;"
js &= " }"
js &= " else"
js &= " {"
js &= " return false;"
js &= " }"
js &= " " & Me.UniqueID & "_KeyPressStatus=true;"
js &= "}"
js &= "</script>"
Return js
End Function
Private Function getJS4KeyPress() As String
Dim js As String
js = "<script language="javascript">"
js &= "function KeyPressed() "
js &= "{"
js &= " " & Me.UniqueID & "_KeyPressStatus=true;"
js &= "}"
js &= "</script>"
Return js
End Function
Finally, we override the RenderBeginTag and RenderEndTag passing in the AddAttributesToRender to let our changes take effect.
Public Overrides Sub RenderBeginTag(ByVal writer As _
System.Web.UI.HtmlTextWriter)
If Not DesignMode Then AddAttributesToRender(writer)
writer.RenderBeginTag(HtmlTextWriterTag.Input)
End Sub
Public Overrides Sub RenderEndTag(ByVal writer As _
System.Web.UI.HtmlTextWriter)
writer.RenderEndTag()
End Sub
Now lets work on the validation part. We are going to use the standard asp.net validation controls for two reasons.
One is to allow for interaction with the validation summary control and two, becuase its really much easier to work
with these already made validation controls than to implement our own javascript validation methods. So lets add
the following properties to our textbox:
<Category("Validation"), Browsable(True)> _
Public Property IsRequired() As Boolean
Get
If ViewState("IsCompulsory") Is Nothing Then
ViewState("IsCompulsory") = False
End If
Return ViewState("IsCompulsory")
End Get
Set(ByVal Value As Boolean)
ViewState("IsCompulsory") = Value
End Set
End Property
<Category("Validation"), Browsable(True)> _
Public Property IsRequiredErrorMessage() As String
Get
If ViewState("IsRequiredErrorMessage") Is Nothing Then
ViewState("IsRequiredErrorMessage") = ""
End If
Return ViewState("IsRequiredErrorMessage")
End Get
Set(ByVal Value As String)
ViewState("IsRequiredErrorMessage") = Value
End Set
End Property
<Category("Validation"), Browsable(True)> _
Public Property MinimumValue() As String
Get
If ViewState("MinimumValue") Is Nothing Then
ViewState("MinimumValue") = ""
End If
Return ViewState("MinimumValue")
End Get
Set(ByVal Value As String)
ViewState("MinimumValue") = Value
End Set
End Property
<Category("Validation"), Browsable(True)> _
Public Property MaximumValue() As String
Get
If ViewState("MaximumValue") Is Nothing Then
ViewState("MaximumValue") = ""
End If
Return ViewState("MaximumValue")
End Get
Set(ByVal Value As String)
ViewState("MaximumValue") = Value
End Set
End Property
<Category("Validation"), Browsable(True)> _
Public Property RangeErrorMessage() As String
Get
If ViewState("RangeErrorMessage") Is Nothing Then
ViewState("RangeErrorMessage") = ""
End If
Return ViewState("RangeErrorMessage")
End Get
Set(ByVal Value As String)
ViewState("RangeErrorMessage") = Value
End Set
End Property
<Category("Validation"), Browsable(True)> _
Public Property EmailErrorMessage() As String
Get
If ViewState("EmailErrorMessage") Is Nothing Then
ViewState("EmailErrorMessage") = ""
End If
Return ViewState("EmailErrorMessage")
End Get
Set(ByVal Value As String)
ViewState("EmailErrorMessage") = Value
End Set
End Property
<Category("Validation"), Browsable(True)> _
Public Property ErrorDisplayMode() As ErrorMode
Get
If ViewState("ErrorDisplayMode") Is Nothing Then
ViewState("ErrorDisplayMode") = ErrorMode.None
End If
Return ViewState("ErrorDisplayMode")
End Get
Set(ByVal Value As ErrorMode)
ViewState("ErrorDisplayMode") = Value
End Set
End Property
<Category("Validation"), Browsable(True), _
DefaultValue(GetType(WebControls.Style), Nothing)> _
Public Property ErrorStyle() As WebControls.Style
Get
If ViewState("ErrorStyle") Is Nothing Then
ViewState("ErrorStyle") = New WebControls.Style
End If
Return ViewState("ErrorStyle")
End Get
Set(ByVal Value As WebControls.Style)
ViewState("ErrorStyle") = Value
End Set
End Property
Note the use of the Category attribute which specified that a new group to be created having the name "Validation" in the Properties window in visual studio.
Next thing to do is to update our AddAttributesToRender method with the new validation changes by adding the following to the end of the method:
If IsRequired Then
Page.RegisterStartupScript(Me.UniqueID & "_LostFocus", _
getJS4ErrorLostFocus(Me.UniqueID & "_LostFocus"))
writer.AddAttribute("onBlur", Me.UniqueID & "_LostFocus();")
If Not Page.IsStartupScriptRegistered(Me.UniqueID & _
"_RequiredValidation") Then
Page.RegisterStartupScript(Me.UniqueID & _
"_RequiredValidation", getJS4RequiredValidation)
End If
End If
The javascript methods called are implemented below:
Private Function getJS4ErrorLostFocus( _
ByVal JSFunctionName As String) As String
Dim js As String
js = "<script language="javascript">"
js &= "function " & JSFunctionName & "() "
js &= "{"
js &= "event.srcElement.style.backgroundColor='white'; "
js &= "event.srcElement.style.borderColor='DarkGray'; "
If RangeValidator IsNot Nothing Then
js &= "if (event.srcElement.value.length==0 ||" & _
!document.getElementById('" & _
RangeValidator.ClientID & "').isvalid) "
Else
js &= "if (event.srcElement.value.length==0) "
End If
js &= "{"
js &= "event.srcElement.style.borderColor='Red'; "
js &= "event.srcElement.style.backgroundColor='#FFC0C0'; "
js &= "}"
js &= "}"
js &= "</script>"
Return js
End Function
Private Function getJS4RequiredValidation() As String
Dim js As String
js = "<script language="javascript">"
js &= "function " & Me.UniqueID & _
"_RequiredValidation(source,args) "
js &= "{"
js &= "if (!isVisible(document.getElementById('" & _
Me.ClientID & "')))" & _
{args.IsValid = true; return true;}"
js &= "if (args.Value.length==0 ) "
js &= "{"
js &= "document.getElementById('" & Me.ClientID & "')" & _
.style.borderColor='Red'; "
js &= "document.getElementById('" & Me.ClientID & "')" & _
.style.backgroundColor='#FFC0C0'; "
js &= "args.IsValid = false;"
js &= "} else { args.IsValid = true; }"
js &= "}"
js &= "</script>"
Return js
End Function
Private Function getJS4ValidatorChange( _
ByVal Validator As WebControls.BaseValidator) As String
Dim js As String
js = "var txt = document.getElementById('" & _
Me.ClientID & "');"
js &= "var vd = document.getElementById('" & _
Validator.ClientID & "');"
js &= "if (!vd.isvalid)"
js &= "{ "
js &= "txt.style.borderColor='Red'; "
js &= "txt.style.backgroundColor='#FFC0C0';"
js &= "}"
js &= "else"
js &= "{"
js &= "txt.style.borderColor='DarkGray';" & _
txt.style.backgroundColor='white';"
js &= "s}"
Return js
End Function
To test which validators are set by the developer, we need to handle the Init event of the textbox and call the appropriate methods. Below is the simple implementation
of the init method that checks for enabled validators:
Private Sub SwdTextBox_Init(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Init
If Not DesignMode Then
If IsRequired Then
AddRequiredValidation()
End If
If InputType = InputTypeMode.Digits OrElse _
InputType = InputTypeMode.Decimal OrElse _
InputType = InputTypeMode.Date Then
If MinimumValue <> "" AndAlso MaximumValue <> "" Then
AddRangeValidation()
End If
ElseIf InputType = InputTypeMode.Email Then
AddEmailValidation()
End If
End If
End Sub
The methods called above are implemented below using the standard asp.net validators for the reasons mentioned before. We also need to add and enumeration of the
validator's error display mode(static,dynamic,none...). Below is the implementation of these methods:
Public Enum ErrorMode As Byte
[None] = 1
[Static] = 2
[Dynamic] = 3
End Enum
Private Sub AddRequiredValidation()
ReqValidator = New WebControls.CustomValidator
Select Case ErrorDisplayMode
Case ErrorMode.None
ReqValidator.Display = ValidatorDisplay.None
Case ErrorMode.Static
ReqValidator.Display = ValidatorDisplay.Static
Case ErrorMode.Dynamic
ReqValidator.Display = ValidatorDisplay.Dynamic
End Select
ReqValidator.ErrorMessage = IsRequiredErrorMessage
ReqValidator.ControlToValidate = Me.ID
ReqValidator.ClientValidationFunction = _
Me.UniqueID & "_RequiredValidation"
ReqValidator.ValidateEmptyText = True
ReqValidator.ValidationGroup = Me.ValidationGroup
Controls.Add(ReqValidator)
End Sub
Private Sub AddRangeValidation()
RangeValidator = New WebControls.RangeValidator
Select Case ErrorDisplayMode
Case ErrorMode.None
RangeValidator.Display = ValidatorDisplay.None
Case ErrorMode.Static
RangeValidator.Display = ValidatorDisplay.Static
Case ErrorMode.Dynamic
RangeValidator.Display = ValidatorDisplay.Dynamic
End Select
RangeValidator.ErrorMessage = RangeErrorMessage
RangeValidator.ControlToValidate = Me.ID
RangeValidator.MinimumValue = MinimumValue
RangeValidator.MaximumValue = MaximumValue
RangeValidator.ValidationGroup = Me.ValidationGroup
Select Case InputType
Case InputTypeMode.Digits
RangeValidator.Type = ValidationDataType.Integer
Case InputTypeMode.Decimal
RangeValidator.Type = ValidationDataType.Double
Case InputTypeMode.Date
RangeValidator.Type = ValidationDataType.Date
End Select
Controls.Add(RangeValidator)
RangeValidator.Attributes.Add("onpropertychange", _
getJS4ValidatorChange(RangeValidator))
End Sub
Private Sub AddEmailValidation()
EmailValidator = New WebControls.RegularExpressionValidator
Select Case ErrorDisplayMode
Case ErrorMode.None
EmailValidator.Display = ValidatorDisplay.None
Case ErrorMode.Static
EmailValidator.Display = ValidatorDisplay.Static
Case ErrorMode.Dynamic
EmailValidator.Display = ValidatorDisplay.Dynamic
End Select
EmailValidator.ErrorMessage = EmailErrorMessage
EmailValidator.ControlToValidate = Me.ID
EmailValidator.ValidationExpression = _
"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"
EmailValidator.ValidationGroup = Me.ValidationGroup
Controls.Add(EmailValidator)
EmailValidator.Attributes.Add("onpropertychange", _
getJS4ValidatorChange(EmailValidator))
End Sub