Building ASP.NET TextBox with Integrated Validation And Switchable Input Modes






4.43/5 (5 votes)
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 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
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