Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Self Validating ASP.NET Text Box

0.00/5 (No votes)
6 Oct 2003 1  
How to extend web form controls to be self validating so you don't have to mess with validation controls.

Introduction

With the richness of .NET, I want more. I want web form controls in ASP.NET that have built in validation -- no messing with all the validation controls! You should be able to just create an IntegerTextBox or a MoneyTextBox, etc. and specify that it won't accept being blank or that it has a value within a range, etc. Plus, it should return the value in the right format so I don't have to mess with it. I would even like it to change colors when there is an issue.

So, I have struggled to figure out how to do this so you won't have to.

Implementing the IValidator interface

I delved into the SDK documentation and discovered that any control can be a page validator if it implements the IValidator interface. Here is a simple example derived from TextBox.

using System;
using System.Web.UI.WebControls;
using System.Web.UI;

namespace MyValidatingControls {
   public class TextBox : System.Web.UI.WebControls.TextBox, IValidator {
      private bool _valid = true;
      private string _errorMessage = "";

      public bool IsValid {
         get { return _valid; }
         set { _valid = value; }
      }

      public string ErrorMessage {
         get { return _errorMessage; }
         set { _errorMessage = value; }
      }

      public void Validate() {
      }
   }
}

Of course this doesn't do anything, but the code above fulfills the basic IValidator contract (i.e. it will compile). I created two private fields to hold the state and the error message, if there is one. To ensure our validator is executed, we have to add our validator to the list of page validators for the containing page.

I did a little homework reading the SDK and it mentions that validators add themselves to the list during initialization. IValidators basically register themselves. So, we override OnInit and OnUnload to add and remove our validator from the page's Validators list.

protected override void OnInit(EventArgs e) {
   base.OnInit(e);
   Page.Validators.Add(this);
}

protected override void OnUnload(EventArgs e) {
   if (Page != null) {
      Page.Validators.Remove(this);
   }
   base.OnUnload(e);
}

Finishing the "Setup"

Before we can write our validator, I want to setup a few more helper items that will make things a little neater. I don't want to have to provide error messages based on each one of the validation issues. I will program those into the control, based on what type of data it should expect. Therefore, I need to provide a little more information to the control so that it can properly give error messages.

I will add a property called FriendlyName which will be used in all error messages to refer back to this control. So, if the control we are asking for has an ID of RetailPrice we make our FriendlyName be Retail Price or whatever it is labeled on the page.

private string _friendlyName = "";

public string FriendlyName {
   get { return _friendlyName; }
   set { _friendlyName = value; }
}

Last, before we write our validation, I want to update IsValid to change my control to a light red color if it is invalid.

public bool IsValid {
   get { return _valid; }
   set { 
      _valid = value; 
      if (!_valid) {
         this.BackColor = Color.LightCoral;
      } 
      else {
         this.BackColor = Color.White;
      }
   }
}

No blanks allowed

For our first validation, lets make it optional that the text field won't accept blanks. We need to make a property that can be set to "enable" this validation.

private bool _blankAllowed = true;

public bool AllowBlank {
    get { return _blankAllowed; }
    set { _blankAllowed = value; }
}

Finally, we can write the Validation function and place it on a web page.

public virtual void Validate() {
   this.IsValid = true;

   if (!this.AllowBlank) {
      bool isBlank = (this.Text.Trim() == "");

      if (isBlank) {
         this.ErrorMessage = 
             String.Format("'{0}' cannot be blank.", 
             this.FriendlyName);
         this.IsValid = false;
      }
   }
}

Expanding on the idea

Now that we have a basic text field with built-in validation, we can expand on the idea and create more interesting validating text box controls.

The next simple expansion on the idea would be an IntegerTextBox. I want my IntegerTextBox to set the range of valid values, but still we can allow for blanks. So, like before, we add the needed properties.

Since we built the basic TextBox, we only need derive from it and override the Validate and add new properties and we are in business.

private int _minValue = Int32.MinValue;
private int _maxValue = Int32.MaxValue;

public int MinValue {
   get { return _minValue; }
   set { 
      _minValue = value; 
      
      if (_minValue > _maxValue) {
          int swap = _minValue;
          _minValue = _maxValue;
          _maxValue = swap;
      }
   }
}

public int MaxValue {
   get { return _maxValue; }
   set { 
      _maxValue = value; 
      
      if (_minValue > _maxValue) {
          int swap = _minValue;
          _minValue = _maxValue;
          _maxValue = swap;
      }
   }
}

Then, we update our Validate method and add a native value output.

public override void Validate() {
   this.IsValid = true;

   bool isBlank = (this.Text.Trim() == "");

   if (isBlank) { 
      if (!AllowBlank) {
         this.ErrorMessage = String.Format("'{0}' " +
            "cannot be blank.", this.FriendlyName);
         this.IsValid = false;
      }
   } else {
      try {
         _value = Int32.Parse(this.Text);
         if (_value < this.MinValue) {
            this.ErrorMessage = String.Format("'{0}' cannot " +
              "be less than {1}", 
              this.FriendlyName, this.MinValue);
            this.IsValid = false;
         }
         
         if (_value > this.MaxValue) {
            this.ErrorMessage = String.Format("'{0}' " +
               "cannot be more than {1}", 
               this.FriendlyName, this.MinValue);
            this.IsValid = false;
         }
      } catch {
         this.ErrorMessage = String.Format("'{0}' " +
              "is not a valid integer.", this.FriendlyName);
         this.IsValid = false;
      }
   }
}

public int Value {
   get { return _value; }
   set { 
      _value = value;
      this.Text = _value.ToString();
   }
}

Conclusion

That's about it! Just extend on this class and you can create a DateTextBox or a CurrencyTextBox all with built-in validation. I have included a sample usage page that shows how nice it works.

Before we had:

<asp:TextBox id="Number" runat="server"/>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" 
   ControlToValidate="Number" 
   Text="'Number' cannot be blank." runat="server"/>
<asp:RangeValidator id="Range1" ControlToValidate="Number" 
   MinimumValue="0" MaximumValue="100" 
   Type="Integer" Text="The value must be from 0 to 100!" 
   runat="server"/>

Now we have:

<MyControls:IntegerText id="Number" 
    FriendlyName="Number" MinValue="0" MaxValue="100" 
    AllowBlank="false" runat="server">

Mind you, my classes are far from finished nor does it provide the exact functionality as existing validation controls. One definite improvement would be to add client side scripting so that all validation doesn't happen on the server.

But, for someone like myself who doesn't use Visual Studio .NET, this can save a lot of typing and setting of properties.

I wanted everyone to see an example of how this is done, in case you are looking for the same kind of thing. Maybe in the future I will feel inspired enough to show you the completed set of classes I use to do this with.

History

I noticed in the SDK that adding the validator should occur in Init and not Load. So, I updated the text and code samples. I also added remove.

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