My goals in building this project is to build a simple web control that will inherit the basic functionality of a standard web text box, but will also wrap the validation controls into the mix. So. No more dragging’ two controls onto the page. Now I just drag out one, set the properties, and away we go. This article owes a great debt to Patick Meyer, and his article on self validating text boxes.
As a secondary goal, I wanted to finally get started on building my own web control library. As an aside, perhaps the easiest way to do this is to simply inherit from all the current web controls, then build a dll. Voilà – your own extensible code library. Not very exciting, but it is a start.
So, back to basics. When you are building controls (web or not-web) you have a couple of options. You can open a web control project, and start coding. This is great if you need to start from scratch and build some wild must-have control, but I am just trying to build a self validating text box here. Building a free form web control is a project for another day.
Most of the functionality I will need is already in the Microsoft text box class. Let’s not reinvent the wheel (See the title of my blog for more info.) We are going to inherit from the standard text box control instead. So open that web control project, and give it an appropriate name. I chose:
MyCompany.Web.UI.WebControls
This is how Microsoft sets up their controls, so I am going to run with it for now. Notice that I named the project MyCompany.Web.UI.WebControls. This will save you heartache later if you keep the namespace names the same as the project names.
Now we know we are going to inherit from text box, but the project template throws up a more generic WebControl class. Change this:
public class WebControl1 : System.Web.UI.WebControls.WebControl
To this:
public class ValidatingTextBox : System.Web.UI.WebControls.TextBox
While you are in there you can remove the Text property. Since we are inheriting from the text box, we don’t need it anymore. The text box already knows has a perfectly serviceable text property. For most inherited controls, I would also suggest removing the render control, but not for this one. We are going to use it later to emulate some of the standard validator’s functions.
Now here comes the tricky part. We want to add in validation capability. Ideally we would inherit from a validator as well. However, .Net does not support multiple inheritance (for Multi-language compatibility reasons, mainly.) But we can use interfaces, and there is an IValidator interface that will work.
So, add to the class declaration so it looks like this:
public class ValidatingTextBox : System.Web.UI.WebControls.TextBox, IValidator
The addition of an interface is like signing a contract. It is an agreement that the class will implement at the very least all the methods and properties required by the interface. In the case of the IValidator, the two properties are IsValid (surprise, surprise) and ErrorMessage. The method is (again not very surprisingly) Validate(). So I will add these right away. (If you are working with a later version of Visual Studio, It will add the stubs for you.) Along with the properties I will add the appropriate stubs
public bool IsValid {
get { return mIsValid; }
set { mIsValid = value; }
}
public string ErrorMessage {
get { return mErrorMessage; }
set { mErrorMessage = value; }
}
public virtual void Validate() {
this.IsValid = true;
}
There are the stubs – but they ain’t going to do much, as is. We need to add in the guts now. Specifically, I’d like to be able to control how the error message is rendered, I’d like to be able to turn on or off the entry required for this control, and I’d like to use a regular expression to check for validity.
Let’s do those in order:
Controlling how the error message is rendered:
If you have used the Validators at all you should be familiar with the Display property. It can be set to three values: Static, Dynamic, and None where static always shows the error message, dynamic shows it when there is an error, and none only shows the message in the validation summary. (While this is a simplification, this will be the behavior I try to duplicate.)
Let’s add an enum for these three properties, and set up a private variable of type of this enum, and set up a property to get and set this private variable:
public enum DisplayType { Static, Dynamic, None }
private
DisplayType mDisplay;
public DisplayType Display {
get {return mDisplay;}
set{mDisplay = value;}
}
Next, override the render method of the control. (I told you we would use it later!) We will simply take the base class rendering and rewrite it - with one small exception. We will add the error message to the control depending on what we have chosen for the display property.
protected override void Render(HtmlTextWriter output) {
//get the rendering from the base (text box ) control
base.Render(output);
//make sure we have an error message
if (this.ErrorMessage != null && this.ErrorMessage != "" ) {
//Set up the HTML for the error message.
//If you want, you can set up properties that will allow
//the developer to change the font, but for now
//we are sticking with a red error message
string formattedMessage = " " + ErrorMessage + "";
//figure out how the user wants the message rendered
switch (Display) {
case DisplayType.Dynamic :
//check to see if the control is valid
//if it is don't display the message
if (!IsValid) {
output.Write(formattedMessage);
}
break;
case DisplayType.Static :
//always display the message
output.Write(formattedMessage);
break;
//since we do nothing here, this case is not
//really needed, but is included for completeness
case DisplayType.None:
break;
}
}
}
Turn On and Off the entry requirements for this control:
Another property! This part is actually pretty easy. This time the property will contain the field IsEntryRequired. If IsEntryRequired is true and there is nothing in the text box then the control isn’t valid. We will check this during the validation process.
public bool IsEntryRequired {
get {return mIsEntryRequired;}
set {mIsEntryRequired = value;}
}
Use a regular expression for validation:
Add in another property – this one will contain the regular expression used to evaluate the text box’s input text.
public string RegExpression {
get {return mRegExpression ;}
set {mRegExpression = value;}
}
Oh – and since we are using regular expressions don’t forget:
using
System.Text;
using
System.Text.RegularExpressions;
OK – we are moving right along here. Now we gotta tie it all together. We will do that using the Validate method. During this process the method will check all its settings and determine if the control is valid or not, and then update the client accordingly. You will need to call this method from your data submit button. This means that the validation is occurring at the server. More on this later.
public
virtual void Validate() {
//initialize IS valid to true
//we will only then check for false conditions
this.IsValid = true;
//determine if we even have any data to check
bool noData = (this.Text.Trim()==string.Empty );
//get the low hanging fruit here - see if
//the control needs an entry but doesn't have one
if (IsEntryRequired && noData) {
this.IsValid = false;
}
//only perform this next stuff if we need to
if (IsValid && !noData && (this.RegExpression != null || this.RegExpression != "")) {
Regex validCheck =
new Regex(RegExpression);
this.IsValid = validCheck.IsMatch(this.Text.Trim());
}
}
To get this to work with the page (and more importantly the validation summary) you have to let the page know it has a control with validation capability. Do this by adding it to the Page's Validator's collection when the control Initializes. Basically the OnInit is part of the base class (Text Box) functionality, you are just extending it.
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);
}
So all that being done, I want to point out a major shortcoming with this control. Every validation attempt means a round trip to the server. Ideally the control would send some JavaScript to the client when the page is initialized. With some study, I think I can figure out how to avoid this shortcoming. The link to the MSDN IValidator; documentation has some tantalizing hints on how to go about this. But I need to learn much more about JavaScript and regular expressions;, and how the validator controls interrelate with JavaScript. Looks like I will get out the reflector tool and decompile those validator controls.
You now have your own control. The next article will be on how to put it into Visual Studio. After that – more controls; specifically I want to build one that strips out Cross site scripting and / or SQL injection characters.
Complete code follows:
using
System;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.ComponentModel;
using
System.Text;
using
System.Text.RegularExpressions;
namespace
MyCompany.Web.UI.WebControls {
[DefaultProperty("ErrorMessage"),
ToolboxData("<{0}:ValidatingTextBox runat=server>")]
public class ValidatingTextBox : System.Web.UI.WebControls.TextBox {
private bool mIsValid = true;
private string mErrorMessage;
private DisplayType mDisplay;
private bool mIsEntryRequired;
private string mRegExpression;
public enum DisplayType { Static, Dynamic, None }
public string RegExpression {
get {return mRegExpression ;}
set {mRegExpression = value;}
}
public bool IsEntryRequired {
get {return mIsEntryRequired;}
set {mIsEntryRequired = value;}
}
public DisplayType Display {
get {return mDisplay;}
set{mDisplay = value;}
}
public bool IsValid {
get { return mIsValid; }
set { mIsValid = value; }
}
public string ErrorMessage {
get { return mErrorMessage; }
set { mErrorMessage = value; }
}
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);
}
public virtual void Validate() {
//initialize IS valid to true
//we will only then check for false conditions
this.IsValid = true;
//determine if we even have any data to check
bool noData = (this.Text.Trim()==string.Empty );
//get the low hanging fruit here - see if
//the control needs an entry but doesn't have one
if (IsEntryRequired && noData) {
this.IsValid = false;
}
//only perform this next stuff if we need to
if (IsValid && !noData && (this.RegExpression != null || this.RegExpression != "")) {
Regex validCheck =
new Regex(RegExpression);
this.IsValid = validCheck.IsMatch(this.Text.Trim());
}
}
protected override void Render(HtmlTextWriter output) {
//get the rendering from the base (text box ) control
base.Render(output);
//make sure we have an error message
if (this.ErrorMessage != null && this.ErrorMessage != "" ) {
//Set up the HTML for the error message.
//If you want, you can set up properties that will allow
//the developer to change the font, but for now
//we are sticking with a red error message
string formattedMessage = " " + ErrorMessage + "";
//figure out how the user wants the message rendered
switch (Display) {
case DisplayType.Dynamic :
//check to see if the control is valid
//if it is don't display the message
if (!IsValid) {
output.Write(formattedMessage);
}
break;
case DisplayType.Static :
//always display the message
output.Write(formattedMessage);
break;
//since we do nothing here, this case is not
//really needed, but is included for completeness
case DisplayType.None:
break;
}
}
}
}
}