Table of Content
Validation is one of the most important parts of any website. Nowadays, mostly new era websites are having very rich UI, and are very much user friendly. User-friendly includes everything, very good look n feel, excellent user friendliness and well placed messages according to the different scenarios.
So here validation becomes one of the key parts of today's new generation websites because it is going to be shown directly to the end users and obviously users are required to pay extra attention to these messages. Also as the applications and users are increasing rapidly, validation also plays a vital role in the security of the website. We'll discuss it later.
So here, I am going to discuss all the validators provided by ASP.NET and for rich UI, I'll also discuss in detail the calloutextendor that is part of the latest AJAX ToolKit.
Earlier in ASP.NET 2.0, we had two different ways to show the validation message. On the page, either we show the message right/left a message in red, else we could have shown them as a validation summary, which requires a lot of extra space for showing the messages on the page which doesn't suit our new age applications any more. We also had an option to show the messages as alert but this also doesn't look good at all. So for all, we can use the calloutextender which has an elegant look n feel.
When we'll be going to use the single validator for multiple controls, positioning of callout extenders is a key part.
- Visual Studio 2005/2008
- Ajax Extension for Visual Studio 2005 (not required for Visual Studio 2008)
- AjaxToolKit
RequiredFieldValidator
RangeValidator
RegularExpressionValidator
CompareValidator
CustomValidator
ValidationSummary
These are five validators provided by ASP.NET. One more control is also provided ValidationSummary
that is used to display the validation messages as a summary, which we'll not be discussing in detail because we are going to put more emphasis on calloutextenders. And we'll discuss all the validators one by one.
RequiredFieldValidator: we can use this validator as the name suggests, to display the message that this is a required field.
RangeValidator: Range validator is used to validate a control specifying the range of values that can be entered in the control. One thing to keep in mind if we format the control value say 9000 to 9,000 then the Range validator doesn't work.
RegularExpressionValidator: This validator is one of the important validators that is used to validate whether the data is being entered in the correct format or not. And we can also validate the charactercount by regex.
CompareValidator: This validator is used to compare the two control's value and show the message accordingly. Also please note here, if we format the numbers as I stated earlier in Range validators, then this validator does not work.
CustomValidator: This validator is one of the key validators that is provided, here we can write our own JS function to validate the controls according to our need.
ValidationSummary: This control is used to show all the validation messages for each failed validator (if any) in popup or in page as per setting.
One more point is that we can use these validators on not only textbox but also several other controls like dropdown, checkbox, etc.
The big question is, how are they more effective? Earlier we generally used JS function to validate our controls on submit of the page or as per our requirement. We will discuss it in detail.
The first and foremost thing is that we don't need to put extra effort to write the js function to validate the controls. We just need to drag and drop ASP.NET validators and set a few properties, that's it. But there is more than this.
Let's say, we have some script code to validate the controls, as we know script gets executed on clientbrowsers so any malicious user can bypass this type of check. So we must have some server side validation. Because server side validation may become very heavy and will make the page get refreshed several times, which is not user friendly at all, ASP.NET validators have the best of both. So here I am going to discuss how ASP.NET validators works.
Validators get fired from the client machine on every change in the control, and also at the time of submission of the page.
So let's look at the server side sequence validation. Let's first look at the sequence of events that get fired during any postback. As we know, when you load the sequence for the first time and the list of events that gets fired are different and there is no scope/use of validation. Validation gets fired on any postback. So let's see the sequence of events on any postback.
Figure: Page Life Cycle on Page Postback
So here, the validation took place on step 5, just before the event fires for the button or control that triggered the validation. In ASP.NET, Button controls have a public
property CausesValidation
that is by default set to true
. It must be true to get the validation fired. We can also set it to false
if we don't want any validation for the control.
Note: One point that must be kept in mind is that the validation does not get evaluated on Page_Load
. It also gets us one benefit - we can programmatically change property values affecting the validity of the page, such as enabling or disabling certain valuators.
But if we explicitly want everything to get executed on the pageload, we can do this specially triggering the method Page.Validate
and it will validate the page. Once it gets called, we can check the status by accessing Page.Isvalid
property. If you try to query the result of Page.IsValid
before Page.Validate
has been called, either explicitly or being triggered by a button with CausesValidation=True
, then its value is meaningless, so an exception will be thrown.
So here, I'll also discuss the architecture of Validation.
The page APIs for the validation are:
Property/Method |
Description |
Validators |
This returns all the validators on the page |
IsValid |
It is set only when Page.Validate gets fired either explicitly called or implicitly. It is true only if all the objects in the Validators collection are valid, and it does not cache this value. |
Validate() |
This method is called at the time of validation. |
Every validator control implements the interface IValidator
.
If client side validation is enabled, a whole different sequence occurs in-between the round trips. Client side validation works with the help of Client JavaScript library. That is added to the solution whenever we use any validator on our page, and looks like:
<script language="javascript"
src="http://www.codeproject.com/
aspnet_client/system_web/1_0_3617_0/WebUIValidation.js">
</script>
From the server point of view, Clientside validation just means that the validators just emit different stuff into HTML. Except this, all the sequence of events are the same. The server side events are still carried out. This has the following reasons.
- Some validation may not support client script, say for custom validator with a server validation function and having no client validation function.
- Server is always mandatory because a malicious user can very easily take a page and disable or change the script. So we cannot rely on it. It is just used for giving the quick response to user. That's why it is always recommended that you should provide client side validation function as well as server side validation functions.
Every validation control script makes sure that some client script is added to the page. But it doesn't mean all the controls get added to the page. Most of the code resides in a script file named as WebUIValidation.js and this reference is added into the page. You can go through this file to see the complete code for these validators. By default, the script file will be installed into the default root in the aspnet_client
directory.
There are cases when one does not want any client side validation. If the number of input fields is very small, client-side validation may not be of much benefit. And also, we may have logic that needs a round trip to the server every time anyway. We may find that the dynamically appearing messages on the client have an adverse affect on your layout.
The way to disable client-side validation is to set the EnableClientScript
property of the validator or ValidationSummary
control to False
. It is possible to have a mixture of server-only and client-sever validation components on the same page.
The clientside sequence of validators are as below:
- When the page gets loaded, on the page every validator gets rendered as
span
tags with HTML attributes that correspond closely to their properties on the server. The main thing that happens here is that any input elements referenced by the validators are hooked up
. The referenced input elements have their client events modified so that the validation routines are called whenever the input is changed and appropriately validation message are shown. We will be using this hookup method to dynamically add the validator to different controls.
- The validation gets executed whenever user changes some values on the input controls.The validation conditions are re-evaluated when a dependant field is changed, and the validator is made visible or invisible as appropriate.
- And also when the user clicks on a button that has a
CausesValidation
property set as true
. The validators are re-evaluated. If they are all valid, then the data is sent to the server, else the following sequence of events occurs:
- The submit of page gets cancelled and page is not sent to the server.
- All invalid validators get visible with appropriate messages.
- If there is a validation summary, then that is visible according to setting with all the failed validators' error messages.
One point here is we can see that validation gets executed atleast two times, once on the client side and then at the server side. This reensures that only valid data is received from the client.
Name |
Type |
Description |
Page_IsValid |
bool |
This is global variable on the page which gives the current status (Valid or Invalid) of the page. This is always in updated state. |
isvalid |
bool |
This is the property associated to every validator and shows the validator is in valid state or not. |
Page_ValidationActive |
bool |
This shows whether validation should take place or not. We can set it to false if we don't want any validation on the page on any specific scenario. |
Page_Validators |
array of elements |
This gives the array of all validators on the page. Actually ASP.NET uses this array to validate the page on any submit. |
ValidatorValidate(val) |
function |
It takes the clientvalidator as input and fires that validator. |
ValidatorEnable(val, enable) |
function |
This function takes the validator and boolean value and accordingly sets the validator, whether enable or disable. |
ValidatorHookupControl(control, val) |
function |
This is the method that takes control and validator as input and associates that validator to the corresponding control. We are going to use this method in our case study beautifully. |
Note: Several times, we want to bypass the validation, say on "Cancel" button click. Then we just need to set the CausesValidation
property to false
of the button. It will not allow to fire any validation event whether on client or server.
All the controls that have validation property can be validated. The validation property is ValidationPropertyAttribute
, which indicates which property should be read for the purposes of validation. And also, only controls that have the value
attribute in the HTML can be validated on the client side.
Gone are the days when the full page was getting submitted on every postback. Now we submit only part of the page that is required to the server. Have a look at the following picture (sample).
Figure: Several Sections of a page
There are 3 section of the page. Only one section needs to postback when user clicks on the respective button. So at the time of partial postback, we may need to validate only few controls that are posting back. So here, we can set a validation group to the validators that we want to validate on a specific event. Also we require to set the validation group to the button, which is going to submit the page. We may have several buttons on our page which must be submitting different parts of the page on the submission. For that, we can use different validation groups to the validators, required to be submitted on the specific postbacks and associate those groups to the appropriate buttons.
By default, if we don't use the validation group or we don't assign a validation group to the button, then all the validators will be evaluated on the submission.
Callout extender is a very good extender control that is provided by the Ajax Toolkit, which gives a very elegant look n feel to our validation. The sample looks like:
Figure: Sample callout extender
And it also plays a vital role in today's rich UI websites. We can attach an extender to any of our validators like:
Here, we need to set only TargetControlID
property and that is set to the ID of the validator. This gives a rich look and feel and is more user friendly.
On some events, we have to execute some JavaScript code before we submit the page. And also, we may require to execute this code only after the validation. So one point needs to be kept in mind. The client side validation is evaluated only after all the JavaScript code execution that we put. So at some point, we first need to validate the page, then we want to execute our JavaScript function. For that, we have a Javascript function
Page_ClientValidate()
that we can call. Actually this validates all the controls on the page and returns
true
and
false
accordingly. On the basis of it, we can execute our JavaScript function.
Also if we want to validate only specific groups, then we can pass the groupname to Page_ClientValidate
function as Page_ClientValidate('groupname')
.
In one of my projects, I had a grid. That grid's datasource was coming from a database. In a normal scenario, the grid contained around 20 rows and around 10 columns. And every column had a textbox. In these textboxes, we had to apply lots of validation, some was normal validation like required, numbers only, range, etc. some of business rules. So we associated 3 or 4 validators to that single textbox. In 20 rows, we had around 20*10*4 validators which is around 800 validators. In some scenarios, the row count may reach 50. At that point of time, the validators count reached up to 2000. And of course, our QA guys added some time around more than 100 rows. So you can assume what would be the size of my page, around 2000 spans were getting rendered and sometimes we did not even require to validate because validation was only required when user tried to change some value. We had to validate all at the Client side. In some cases, my page was like hanged and generally it was taking too long for rendering. Our client refused to accept this speed of the page.
So then, we did some research and explored the ASP.NET validators in depth and found a way to use just one validator to all of our grid's textbox
es. I want to share our learning with you all.
So what we did here:
- We took just one custom validator for all the textboxes.
- We attached the validator on "
onfocus
" event of the textboxes.
- We also did lots of customization regarding error messages and used several client validation functions depending on our business requirement.
- We also assigned different group names based on different events.
Figure: Single validator to multiple Controls
As you can see in the above figure, there is only one validator which I am associating with multiple controls in the onfocus
event.
I am just explaining a sample code for that.
In my example, I have taken a repeater, which I am binding from database, which has two columns. One is Item Name and the other one is its cost. So here, the user can update the cost. I have used a label for Name and a textbox for entering cost, both are in ItemTemplate
. I have put all the validation on these textboxes and validate them at the client side. I can have 10 to 100 and more textboxes in my repeater based on the datasource. Here I am using just one validator for all the textboxes whatever the count may be. Here, I have several kinds of validation on it, like cost greater than $100, it must be numeric and cannot be a blank.
One very important thing is also that I am using calloutvalidator
. So as I am associating the validator control dynamically from client side, so also I have to modify the position of the callout.
My aspx code looks like:
In my aspx code, I have one repeater, one custom validator and callout extender. In the repeater itself, I have a textbox
that is going to be validated by the custom validator.
Here, I have three JavaScript functions and a few JavaScript global variables.
var curobjid;
var i=0;
var isValid = true;
function AttachValidator(curObj, validatorClientID)
{
if(isValid)
{
var validationControl = document.getElementById(validatorClientID);
validationControl.controltovalidate = curObj.id;
validationControl.clientvalidationfunction="validatetextbox";
validationControl.validateemptytext = "true";
ValidatorHookupControl(curObj, validationControl);
curobjid=curObj.id;
}
else
{
document.getElementById(curobjid).focus();
}
}
function validatetextbox(sender, args)
{
if(args.Value=="")
{
sender.errormessage="Required Field Missing
This is required." ;
args.IsValid = isValid = false;
}
else if(isNaN(args.Value))
{
sender.errormessage="Invalid field
Only Numbers are allowed." ;
args.IsValid = isValid = false;
}
else if(Number(args.Value)< 1000)
{
sender.errormessage="value can not be less than 1000
This is required." ;
args.IsValid = isValid = false;
}
if(!args.IsValid)
ShowValidationCallout(sender, curobjid);
}
function ShowValidationCallout(validationControl, elementClientID)
{
if(validationControl.ValidatorCalloutBehavior != null)
{
if(validationControl.ValidatorCalloutBehavior._popupBehavior != null)
{
validationControl.ValidatorCalloutBehavior.
_popupBehavior.set_parentElementID(elementClientID)
}
if(validationControl.ValidatorCalloutBehavior._errorMessageCell !=null)
{
validationControl.ValidatorCalloutBehavior.
_errorMessageCell.innerHTML = validationControl.errormessage;
validationControl.ValidatorCalloutBehavior.show(true);
}
}
}
The first method will be called on the 'onfocus
' event of the textbox
to associate the validator to the corresponding textbox
.
The second method is the client validation function that is going to validate the textbox
. We can have multiple functions instead of one and that also can be dynamically changed for the textboxes as I stated above.
The last method that I am using here is to position the validator callout. If this is not going to be called, then all validation messages will be shown in a single place. You also can use validation summary.
On my server side, what I am doing here? I am just creating a collection on page load and assigning it as a datasource for the Repeater
and in Itemdatabound
I'm attaching the AttachValidator
method. This takes the textbox
object and validators' client Id as a parameter.
protected void Page_Load(object sender, EventArgs e)
{
Dictionary data = new Dictionary();
data.Add("Item1", "20.4");
data.Add("Item2", "0");
data.Add("Item3", "0");
data.Add("Item4", "12.6");
rpUpdateAdpdetails.DataSource = data;
rpUpdateAdpdetails.DataBind();
}
protected void rpUpdateAdpdetails_ItemDataBound
(object sender, RepeaterItemEventArgs e)
{
Label lblName = (Label)e.Item.FindControl("lblName");
TextBox txtBox = (TextBox)e.Item.FindControl("txtCost");
KeyValuePair data = (KeyValuePair)e.Item.DataItem;
lblName.Text = data.Key;
txtBox.Text = data.Value;
txtBox.Attributes.Add("onfocus", "AttachValidator
(this,'" + gridValidator.ClientID + "')");
}
In another case study, I had a requirement like in my grid I have a column a user can update the cost of any item from a popup window and save it, then that value was getting updated in the grid in a label. Initially, we could have some rows with value 0 at loading time but 0 values were not allowed to be submitted. To achieve this functionality ,at the time submitting of the page I had to validate all the labels in the grid and when I find the first label whose value is zero, I had to show the ValidatorCallout
message there. As we know, we could not validate the labels so what I did is I put a hidden textbox with every label and had the same value as label and applied the validators on that. So I actually iterate through all the hidden textboxes and show the validator whenever I got the first textbox with value zero. Although this looks a bit simple. I just wanted to show how I achieved this. Here too, I faced a lots of problem in positioning the ValidatorCallout
error message.
The method I followed here is that at the time of databinding in a hidden variable, I put the textbox's client Ids comma separated, and I used a custom validator for validation purposes. In the client validation function, I iterated through all the textboxes and when I found the textbox with value zero, I just break from the loop and show the message there.
Here also I had the validation like my earlier case study, for all the textboxes like use only numeric etc., which is implemented as in case study one.
My aspx code looks like:
Here I have taken a Repeater, a custom validator, a ValidatorCalloutExtender
and a Hidden Variable. Repeater has a label and a hidden textbox as discussed above.
Repeater:
Here I am going to bind the data initially. User can update the cost by clicking on it.
CustomValidator:
That I'll be using to show the error message. I'll be attaching this validator to the hidden textbox where I had to show the error message. Here, one thing is that I have not set the property ControlToValidate
. This I'll be setting dynamically.
ValidatorCalloutExtender:
This is for showing the error message.
Hidden Variable:
In this variable, I am putting comma separated ClientIds
of all the hidden textboxes.
Here I have three JS functions:
function openpopup(lblId,tbId)
{
var url="popup.aspx?lblid="+lblId +"&textboxId="+ tbId ;
window.open(url, "OpenWindowChild",'left=70,top=0,width=250, height=150');
}
function validateCost(sender, args)
{
var tbIds= document.getElementById('hdnTextBoxIds').value;
var arrIds=tbIds.split(',');
var validationControl= document.getElementById('gridValidator');
for(var i=0;i"document.getElementById(arrIds[i]);" if(curobj.value="=" validationcontrol.controltovalidate="arrIds[i];" sender.errormessage="Invalid field.Please enter the cost." args.isvalid="false;" if(args.isvalid="=" !="null)" validationcontrol.validatorcalloutbehavior._errormessagecell.innerhtml="validationControl.errormessage;" />
The first method will be called on the onclick
event of the label to open the popup
, which takes parameter Label
's and TestBox
's ClientId
, those will be passed to popup as querstring
. Popup will save the entered value to these controls.
The second method is the client validation function that is going to validate the Label (Actually it is validating the hidden textbox). It is iterating through all the hidden texboxes and checks if there is any 0 value or not. When it gets its first value 0, it just comes out of the loop and shows the error message.
The last method that I am using here is to position the validator callout, if this is not going to be called, then all validation messages will be shown at a single place. You can also use validation summary. At my server side, what I am doing is I am just creating a collection on page load and assigning it as a datasource for the Repeater
and in Itemdatabound
, I'm attaching the AttachValidator
method. This takes the textbox
object and validator's client Id as a parameter. I am also attaching the function openpopup
to the Label
's onclick
event.
protected void Page_Load(object sender, EventArgs e)
{
Dictionary<string,> data = new Dictionary<string,>();
data.Add("Item1", "20.4");
data.Add("Item2", "0");
data.Add("Item3", "0");
data.Add("Item4", "12.6");
rpUpdateAdpdetails.DataSource = data;
rpUpdateAdpdetails.DataBind();
}
protected void rpUpdateAdpdetails_ItemDataBound
(object sender, RepeaterItemEventArgs e)
{
Label lblName = (Label)e.Item.FindControl("lblName");
Label lblCost = (Label)e.Item.FindControl("lblCost");
TextBox txtBox = (TextBox)e.Item.FindControl("txtCost");
lblCost.Attributes.Add("onclick", "javascript:openpopup
('" + lblCost.ClientID + "','" + txtBox.ClientID + "');");
hdnTextBoxIds.Value = hdnTextBoxIds.Value + txtBox.ClientID + ",";
KeyValuePair<string,> data = (KeyValuePair<string,>)e.Item.DataItem;
lblName.Text = data.Key;
txtBox.Text = data.Value;
lblCost.Text = data.Value;
txtBox.Attributes.Add("onfocus", "AttachValidator
(this,'" + gridValidator.ClientID + "')");
}
I also have a another page which works as popup for this, and used to enter the data in the grid.
Note: You can download the full source code from the top of the article.
Feedback is always the key for me. I would request you all to share your feedback and give me some suggestions, which would encourage and help in more writing.