Click here to Skip to main content
14,639,973 members
Articles » Web Development » ASP.NET Controls » General
Technical Blog
Posted 4 Jan 2010

Tagged as

Stats

17.6K views
12 bookmarked

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

Rate this:
4.43 (5 votes)
Please Sign up or sign in to vote.
4.43 (5 votes)
4 Jan 2010CPOL
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
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author


Comments and Discussions

 
Generalgood Pin
Yves6-Apr-10 17:17
MemberYves6-Apr-10 17:17 
General"RangeValidator" is not a type, can't use as expression. Pin
Cable Fan5-Apr-10 16:24
MemberCable Fan5-Apr-10 16:24 
GeneralValidators are not rendered Pin
brianparker12-Jan-10 18:02
Memberbrianparker12-Jan-10 18:02 
GeneralRe: Validators are not rendered Pin
jatinsingh1-Apr-10 20:34
Memberjatinsingh1-Apr-10 20:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.