Click here to Skip to main content
15,890,845 members
Articles / Web Development / ASP.NET
Article

Self Validating Text Box - 2

Rate me:
Please Sign up or sign in to vote.
4.50/5 (15 votes)
9 Feb 20044 min read 133.4K   1.1K   49   18
This article extends self validating Text Box control to other data types and Min and Max value checking.

Introduction

The motivation for this article lies at Self Validating ASP.NET Text Box by Patrick Meyer of NASA in Mission Planning Systems. Please read this excellent article for the self-validating control first. Like many other .NET developers, I like self-validating controls due to their simplicity. Microsoft provides CompareValidator and it serves the same purpose in general. Through self-validating control, I have to do less typing and there are less controls on the web form. This article validates the richness of .NET framework.

Pat showed us how to implement IValidator interface with a TextBox and I struggled to implement data type checking for various data types so that we do not need to implement different controls for various data types.

An excellent tool by Jay Freeman shortened my struggle to implement the same functionality as provided by CompareValidator. You can download this free tool from here. This tool goes through all Microsoft assemblies and somehow it converts IL code into C#. I looked how Microsoft implemented CompareValidator control and I used the same API for range and data type checking. It took me a little time to implement this. Through Jay's tool, you can peep through how great minds at Microsoft do the development.

I desire to have a self-validating TextBox control that can check if the field is a required field or not. This should also perform a min or max value validation by using MinValue and MaxValue properties of the TextBox. I should be able to specify the data type of the input in the text box. Since CompareValidator does this already, Anakrino tool helped me figure out how it was done internally. The data types implemented are String, Currency, Double, Date and Integer.

Implementing the Compare of BaseCompareValidator

After going through the source of various methods in BaseValidator, BaseCompareValidator and CompareValidator, I noticed that I could use BaseCompareValidator.CanConvert method to check the data type validity of the input data. I also need to check for the range or compare the input value for different data types against a given MinValue or MaxValue property of the control. If I define TextBox control to be a Date then my date should be within MinValue or MaxValue. If data type is Currency then my value cannot be greater than say 10,000.

A simple rule that I follow for MinValue or MaxValue is that if these values are blank then I do not need to perform a validation. In real life situation, we would like to bind MinValue and MaxValue to the database. I would recommend using two-way data binding approach for a web form in general. There is an excellent article on this topic here. If you use two-way data binding with self-validating TextBox in your real life projects, you do not have to type much code to unbind data.

We hope that ASP.NET "Whidbey" will implement two way data binding and that will reduce coding for generic type of web forms implementing typed DataSets.

To implement range checking, there is a protected method Compare in BaseCompareValidator class. Since I am already inheriting my TextBox class from System.Web.UI.WebControls.TextBox, C# will not allow multiple inheritance. I had to create another class to get the advantage of this protected method.

C#
public class EADCompare : BaseCompareValidator 
{ 
  public static bool DoCompare(string from, string to, 
     ValidationCompareOperator cmp, ValidationDataType objType) 
  { 
    if (to == null || to.Length == 0) return true; 
    return BaseCompareValidator.Compare(from, to, cmp, objType); 
  } 

  protected override bool EvaluateIsValid() 
  { 
     return true; 
  } 
}

The compiler forces you to implement EvaluateIsValid method. I just return true from this. We are not implementing this class in a true sense to implement a separate validator. We just want to access the Compare method to do the work instead of writing custom code.

To show a little graphic image along side with your text box when your validation fails is a neat idea. I borrowed this implementation by overriding Render method of TextBox as follows:

C#
protected override void Render(HtmlTextWriter writer)
{
    base.Render (writer);
    if (this.ErrorMessage != null && this.ErrorMessage != "" )
        writer.Write(" <img src=\"images/stop.gif\" alt=\"" + 
           this.ErrorMessage + "\")\">");
}

The error message shows up when you hover your mouse on this image. All other validation messages show up in the validation summary control as if you have used Microsoft provided validator controls. Please read Patrick’s article for details.

The validate method of the TextBox provides Required, MinValue and MaxValue validation against data types String, Currency, Double, Date and Integer.

C#
public void Validate()
{
   this.IsValid = true;
   bool isBlank = (this.Text.Trim() == "");
   if (Required)
   {
      if (isBlank)
      {
          this.ErrorMessage = 
            String.Format("'{0}' is a required field.", 
            this.UserFieldName);
          this.IsValid = false;
      }
    }
    if (!isBlank)
    {
         // if not blank then check the datatype of the control.
         bool isOk = 
           BaseCompareValidator.CanConvert(this.Text,GetDataType());
         if (!isOk)
         {
            this.ErrorMessage = 
              String.Format("'{0}' is not a valid data type.", 
              this.UserFieldName);
            this.IsValid = false;
            return;
          }
          // If MinValue is not empty then check if Text is less
          // than MinValue. If yes, validate false.
          isOk = EADCompare.DoCompare(this.Text, MinValue, 
            ValidationCompareOperator.GreaterThanEqual,GetDataType());
          if (!isOk)
          {
             this.ErrorMessage = String.Format("'{0}' " + 
               "can not have value less than {1}", 
               this.UserFieldName, this.MinValue);
             this.IsValid = false;
             return;
           }
           // If MaxValue is not empty then check if Text
           // is more than MinValue. If yes, validate false.
           isOk = EADCompare.DoCompare(this.Text, MaxValue, 
                     ValidationCompareOperator.LessThanEqual, 
                     GetDataType());
           if (!isOk)
           {
              this.ErrorMessage = String.Format("'{0}' " + 
                "can not have value more than {1}", 
                this.UserFieldName, this.MaxValue);
              this.IsValid = false;
              return;
            }
       }
}
ASP.NET
< %@Register TagPrefix="EAD" Namespace="EAD.WebControls" Assembly="General" % > 

To use this TextBox control in your web form, you need to first register the control in your ASPX page. Replace the name of the namespace and assembly, if you put this TextBox class in your favorite namespace and assembly.

A typical syntax of the TextBox in your page will look like this:

ASP.NET
<asp:label id="txtErrorMessage" runat="server" 
    EnableViewState="False"></asp:label>
<EAD:TextBox id="TextBox1" runat="server" UserFieldName="Double Test" 
   Required="False" MinValue="123.456" MaxValue="789023.345" 
   TextType="Double"></EAD:TextBox>
<EAD:TextBox id="Textbox2" runat="server" Width="64px" 
   UserFieldName="Date Test" Required="True" MaxValue="12/12/2000" 
   MinValue="1/1/2000" TextType="Date"></EAD:TextBox>
<EAD:TextBox id="Textbox3" runat="server" Width="64px" 
   UserFieldName="Integer Test" Required="True" MaxValue="100" 
   MinValue="10" TextType="Integer"></EAD:TextBox>
<EAD:TextBox id="Textbox4" runat="server" Width="64px" 
   UserFieldName="Currency Test" Required="True" MaxValue="12,000" 
   MinValue="10,000" TextType="Currency"></EAD:TextBox>
<EAD:TextBox id="Textbox5" runat="server" Width="64px" 
   UserFieldName="String Test" Required="False" MaxValue="P" 
   MinValue="B" TextType="String"></EAD:TextBox>

The attached ZIP file has three files. They are a sample ASPX page, TextBox class and Stop.gif image. The sample page has five different types of validations using different data types.

The screen capture is below:

Sample screenshot

Summary

Self validating controls give you flexibility to attach simple validation at the control itself. The same approach can be extended to other types of web controls. It should be fairly simple and straight forward.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Vikram is an Enterprise Application Architect specializing in EAI, ETL, all relational databases and transforming legacy applications to Microsoft .Net environment. Vikram works for a consulting firm in Research Triangle Park, NC. Vikram has expertise in all relational databases, Cobol, mainframe, OO programming, C, Perl and Linux. C# is a newfound craze for Vikram.

Comments and Discussions

 
GeneralShow Message box Pin
Ngo Xuan Chuong28-Dec-07 20:13
Ngo Xuan Chuong28-Dec-07 20:13 
GeneralThis same control in VB [modified] Pin
brantpeery28-Jul-06 11:03
brantpeery28-Jul-06 11:03 
Here is a this same control in VB.Net instead of C#.
I have added some features like a regular expression validator and embeded the image in the dll. So to use this you will have to embed your error image in your assembly, or changed that bit of code. This code also allows the user to set a custom background color, and the text types are strongly typed as an enum. This has only been tested in ASP.NET 2.0
Enjoy!!!

<br />
Imports System<br />
Imports System.Drawing<br />
Imports System.Collections.Generic<br />
Imports System.ComponentModel<br />
Imports System.Text<br />
Imports System.Web<br />
Imports System.Web.UI<br />
Imports System.Web.UI.WebControls<br />
Imports System.Text.RegularExpressions<br />
<br />
<Assembly: WebResource("error.gif", "image/gif")> <br />
<br />
Namespace Company.Web.UI.WebControls<br />
<br />
    <ToolboxBitmap(GetType(TextBox)), _<br />
    DefaultProperty("Text"), _<br />
    ToolboxData("<{0}:TextBox runat='server' size='20'></{0}:TextBox>")> _<br />
    Public Class ValidatedTextBox<br />
        Inherits TextBox<br />
        Implements IValidator<br />
<br />
        Private _valid As Boolean = True<br />
        Private _errorMessage As String = ""<br />
        Private _required As Boolean = False<br />
        Private _minValue As String = String.Empty<br />
        Private _maxValue As String = String.Empty<br />
<br />
<br />
        Private _friendlyName As String = String.Empty<br />
        Private _errorBackground As Color = Color.LightPink<br />
        Private _dataType As ValidationDataType = ValidationDataType.String<br />
        Private _validationExpression As String<br />
<br />
        '<Bindable(True), Category("Appearance"), DefaultValue(""), Localizable(True)> Property Text() As String<br />
#Region "Properties"<br />
        Public Property DataType() As ValidationDataType<br />
            Get<br />
                Return _dataType<br />
            End Get<br />
            Set(ByVal value As ValidationDataType)<br />
                _dataType = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Validation"), _<br />
         Description("The background color of the textbox if the control is invalid"), _<br />
         DefaultValue("")> _<br />
        Public Property ErrorBackground() As Color<br />
            Get<br />
                Return _errorBackground<br />
            End Get<br />
            Set(ByVal value As Color)<br />
                _errorBackground = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Appearance"), _<br />
        Description("The name that is displayed in the auto generated error messages"), _<br />
        DefaultValue("")> _<br />
      Public Property FriendlyName() As String<br />
            Get<br />
                Return _friendlyName<br />
            End Get<br />
            Set(ByVal value As String)<br />
                _friendlyName = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Validation"), _<br />
         Description("The maximum number or date to validate against"), _<br />
         DefaultValue("")> _<br />
        Public Property MaxValue() As String<br />
            Get<br />
                Return _maxValue<br />
            End Get<br />
            Set(ByVal value As String)<br />
                _maxValue = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Validation"), _<br />
         Description("The minimum number or date to validate against"), _<br />
         DefaultValue("")> _<br />
        Public Property MinValue() As String<br />
            Get<br />
                Return _minValue<br />
            End Get<br />
            Set(ByVal value As String)<br />
                _minValue = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Validation"), _<br />
         Description("If set to true, will display and error if the text property is empty or blank"), _<br />
         DefaultValue("False")> _<br />
        Public Property Required() As Boolean<br />
            Get<br />
                Return _required<br />
            End Get<br />
            Set(ByVal value As Boolean)<br />
                _required = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Validation"), _<br />
         Description("The regular expression to use to validate the control"), _<br />
         DefaultValue("")> _<br />
        Public Property ValidationExpression() As String<br />
            Get<br />
                Return _validationExpression<br />
            End Get<br />
            Set(ByVal value As String)<br />
                _validationExpression = value<br />
            End Set<br />
        End Property<br />
#End Region<br />
<br />
#Region "Methods and Overrides"<br />
        Protected Overrides Sub OnInit(ByVal e As System.EventArgs)<br />
            MyBase.OnInit(e)<br />
            Page.Validators.Add(Me)<br />
        End Sub<br />
        Protected Overrides Sub OnUnload(ByVal e As System.EventArgs)<br />
            If Not IsNothing(Page) Then<br />
                Page.Validators.Remove(Me)<br />
            End If<br />
            MyBase.OnUnload(e)<br />
        End Sub<br />
        Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)<br />
            MyBase.OnPreRender(e)<br />
            If Not _valid Then<br />
                Me.Attributes.Add("title", _errorMessage)<br />
                Me.Focus()<br />
                Me.Style.Add("background", System.Drawing.ColorTranslator.ToHtml(_errorBackground))<br />
            Else<br />
                Me.Attributes.Clear()<br />
            End If<br />
<br />
        End Sub<br />
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)<br />
            MyBase.Render(writer)<br />
<br />
            If Not _valid Then<br />
                writer.WriteBeginTag("img")<br />
                writer.WriteAttribute("src", Page.ClientScript.GetWebResourceUrl(Me.GetType, "error.gif"))<br />
                writer.WriteAttribute("alt", _friendlyName)<br />
                writer.WriteAttribute("title", _errorMessage)<br />
                writer.Write("/>") 'close the tag<br />
            End If<br />
        End Sub<br />
#End Region<br />
#Region "IValidator Contract"<br />
        <Category("Data"), _<br />
         Description("The Error Validator Message that will be reported to the validator summary."), _<br />
         DefaultValue("")> _<br />
         Public Property ErrorMessage() As String Implements System.Web.UI.IValidator.ErrorMessage<br />
            Get<br />
                Return _errorMessage<br />
            End Get<br />
            Set(ByVal value As String)<br />
                _errorMessage = value<br />
            End Set<br />
        End Property<br />
<br />
        <Category("Data"), _<br />
         Description("Reports whether the control matches the validation rules"), _<br />
         DefaultValue("")> _<br />
        Public Property IsValid() As Boolean Implements System.Web.UI.IValidator.IsValid<br />
            Get<br />
                Return _valid<br />
            End Get<br />
            Set(ByVal value As Boolean)<br />
                _valid = value<br />
            End Set<br />
        End Property<br />
<br />
        Public Sub Validate() Implements System.Web.UI.IValidator.Validate<br />
            _valid = True<br />
            Dim isBlank As Boolean = IIf(Me.Text.Trim = "", True, False)<br />
<br />
            If Not isBlank Then 'We have a value, so validate it<br />
                'Check to see if it is the right data type<br />
                _valid = BaseCompareValidator.CanConvert(Me.Text, _dataType)<br />
                If Not _valid Then<br />
                    Dim TypeName As String<br />
                    'Give the type a friendly name<br />
                    Select Case _dataType<br />
                        Case ValidationDataType.Currency<br />
                            TypeName = "decimal number"<br />
                        Case ValidationDataType.Date<br />
                            TypeName = "date"<br />
                        Case ValidationDataType.Double<br />
                            TypeName = "decimal number"<br />
                        Case ValidationDataType.Integer<br />
                            TypeName = "number with no decimal places"<br />
                        Case Else<br />
                            TypeName = "[type conversion error]" 'We should NEVER get this<br />
                    End Select<br />
                    _errorMessage = "The value entered in " & _friendlyName & " is not a valid " & TypeName & "."<br />
                    Return 'get out of the sub<br />
                End If<br />
<br />
                'Check the min value range<br />
                _valid = MyBaseCompareValidator.DoCompare(Me.Text, _minValue, ValidationCompareOperator.GreaterThanEqual, _dataType)<br />
                If Not _valid Then<br />
                    _errorMessage = _friendlyName & " cannot be less than " & _minValue<br />
                    Return<br />
                End If<br />
                'Check the max value range<br />
                _valid = MyBaseCompareValidator.DoCompare(Me.Text, _maxValue, ValidationCompareOperator.LessThanEqual, _dataType)<br />
                If Not _valid Then<br />
                    _errorMessage = _friendlyName & " cannot be more than " & _maxValue<br />
                    Return<br />
                End If<br />
                'Do the regular expression validation if it is a string and the regex is set<br />
                '--if it is not valid, the error message set by the user in ErrorMessage will be displayed<br />
                _valid = RegExValidate()<br />
<br />
            ElseIf _required Then 'It is blank and required<br />
                _errorMessage = _friendlyName & " is a required field"<br />
                _valid=false<br />
                Return<br />
            End If<br />
<br />
        End Sub<br />
#End Region<br />
<br />
#Region "Helper Functions"<br />
        Private Function RegExValidate()<br />
            If ((Me.Text Is Nothing) OrElse (Me.Text.Trim.Length = 0)) Then<br />
                Return True<br />
            End If<br />
            Try<br />
                Dim match1 As Match = Regex.Match(Me.Text, _validationExpression)<br />
                Return ((match1.Success AndAlso (match1.Index = 0)) AndAlso (match1.Length = Me.Text.Length))<br />
            Catch ex As System.Exception<br />
                Return True<br />
            End Try<br />
<br />
        End Function<br />
#End Region<br />
    End Class<br />
<br />
    Friend Class MyBaseCompareValidator<br />
        Inherits BaseCompareValidator<br />
<br />
        Public Shared Function DoCompare(ByVal source As String, ByVal compareTo As String, _<br />
        ByVal compareOp As ValidationCompareOperator, ByVal compareType As ValidationDataType) As Boolean<br />
            'Check to see if the value to compare to is blank<br />
            If compareTo.Trim.Length = 0 Then<br />
                'Lets just return true because the limit was never set<br />
                Return True<br />
            Else<br />
                Return BaseCompareValidator.Compare(source, compareTo, compareOp, compareType)<br />
            End If<br />
        End Function<br />
        Protected Overrides Function EvaluateIsValid() As Boolean<br />
            'not used<br />
        End Function<br />
    End Class<br />
End Namespace<br />
<br />




-- modified at 17:22 Friday 28th July, 2006
GeneralDoes not support ValidationGroup Pin
Sire40412-Apr-05 1:41
Sire40412-Apr-05 1:41 
GeneralRe: Does not support ValidationGroup Pin
Anonymous8-Jun-05 5:53
Anonymous8-Jun-05 5:53 
GeneralRe: Does not support ValidationGroup Pin
foosball31610-Feb-06 12:21
foosball31610-Feb-06 12:21 
AnswerRe: Does not support ValidationGroup Pin
Marcelo Godoy21-Dec-06 0:01
Marcelo Godoy21-Dec-06 0:01 
QuestionRe: Does not support ValidationGroup [modified] Pin
tumay10-May-07 8:20
tumay10-May-07 8:20 
GeneralRe: Does not support ValidationGroup Pin
DaveOMacalroy24-Jul-07 1:28
DaveOMacalroy24-Jul-07 1:28 
GeneralRe: Does not support ValidationGroup [Solution] (Kludge but it works) Pin
Sean Savelli7-Sep-07 3:40
Sean Savelli7-Sep-07 3:40 
GeneralRe: Does not support ValidationGroup [Solution] (Kludge but it works) Pin
taliesins30-Jul-08 4:48
taliesins30-Jul-08 4:48 
GeneralAbout Image Position &amp; Page Load Pin
Hemant Mane20-Feb-05 2:47
Hemant Mane20-Feb-05 2:47 
GeneralImage not past behind TextBox Pin
serg_bor1-Feb-05 19:23
serg_bor1-Feb-05 19:23 
GeneralClientValidationFunction Pin
dietrich22-Jun-04 12:14
dietrich22-Jun-04 12:14 
GeneralTrying to enhance control and/or rewrite in VB Pin
dokmanov14-Apr-04 17:58
dokmanov14-Apr-04 17:58 
GeneralIt does not work in usercontrol Pin
tzarski11-Mar-04 0:33
tzarski11-Mar-04 0:33 
GeneralCouldn't not found EAD.WebControls Pin
Mohammed Nayeem16-Feb-04 23:14
Mohammed Nayeem16-Feb-04 23:14 
GeneralRe: Couldn't not found EAD.WebControls Pin
vikramk17-Feb-04 2:23
vikramk17-Feb-04 2:23 
GeneralRe: Couldn't not found EAD.WebControls Pin
Mohammed Nayeem17-Feb-04 18:16
Mohammed Nayeem17-Feb-04 18:16 

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.