Using the CustomValidator Control...

Sometimes one of the predetermined types of validation controls such as a RequiredFieldValidator, or a RangeValidator just will not do the job you need done. In this case you need to write a Custom Validator to achieve the desired results.


By: John Kilgo Date: June 29, 2003 Download the code.

The other (non-custom) validators generate client-side code to validate TextBoxes, DropDownList values, etc. All you do is set properties within the <asp:...Validator block and .NET takes care of the rest. That is fine where one the other validators fits the business rule to be processed against. But sometimes business rules are more complicated. I was working on a program the other day where a TextBox contained a date. The date was not a required field. But if a date was entered it must be valid, it must be equal to or earlier than the current date, and it must be equal to later than another, required, date TextBox on the form. Clearly, none of the other, non-custom validators could validate that field.

In comes the CustomValidator to the rescue. The CustomValidator allows (requires) you, the programmer, to write server-side (or client-side for that matter) code to validate the field. In this article and the example program illustrating it we will see how the CustomValidator can be used to validate using the business rules described in the previous paragraph. We will also point out a couple of quirks (bugs?) of the CustomValidator.

Lets start by looking at the .aspx page called CustomValidator.aspx. It contains three labels, two textboxes, a submit button, a CustomValidator and a ValidationSummary control. A ValidationSummary control allows the display of validation error messages in a central location instead of, or in addition to, displaying messages to the side of the validated controls. The main thing to notice about the code below is that we have set up an OnServerValidate event within the CustomValidator. The method (Validate_Date) will appear in our code-behind page and is where we will write the validation code. Also notice that I have not created a meaningful error message on the form. We will set an appropriate error message in the validation method depending upon the error we find rather than having some one-size-fits-all message.

<%@ Page Language="vb" AutoEventWireup="false" Src="CustomValidator.aspx.vb" Inherits="CustomValidator"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>CustomValidator</title>
<meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body>
<b>Business Rules:</b>
<ol>
<li>The second date is NOT required.</li>
<li>If the second date does have a value it must be equal to or earlier than today.</li>
<li>If the second date does have a value it must be equal to or later than the first date</li>
</ol>
<form id="Form1" method="post" runat="server">
<table>
  <tr>
    <td align="right">
      <asp:Label ID="lblFirstDate" Text="First Date: " Runat="server" />
    </td>
    <td align="left">
      <asp:TextBox ID="txtFirstDate" Text="1/15/2003" runat="server" />
    </td>
  </tr>
  <tr>
    <td align="right">
      <asp:Label ID="lblSecondDate" Text="Second Date: " Runat="server" />
    </td>
    <td align="left">
      <asp:TextBox ID="txtSecondDate" Runat="server" />
      <asp:CustomValidator ID="valSecondDate"
                           Runat="server"
                           ControlToValidate="txtSecondDate"
                           Display="None"
                           OnServerValidate="Validate_Date"
                           ErrorMessage="*" />
    </td>
  <tr>
  <tr>
    <td colspan="2" align="center">
      <asp:Button ID="btnSubmit"
                  Text="Submit"
                  CausesValidation="True"
                  Runat="server" />
    </td>
  </tr>
</table>
<p></p><p></p>
<asp:Label ID="lblMessage" Runat="server" />
<p></p><p></p>
<asp:ValidationSummary ID="vsmErrors"
                       runat="server"
                       DisplayMode="BulletList"
                       ShowSummary="True"
                       Font-Name="Verdana"
                       Font-Size="10pt" />
</form>
</body>
</html>

Now lets look at the code-behind file where we actually program our CustomValidator. 'args' (from ServerValidateEventArgs) gives us two properties which are useful to us. 'IsValid' sets the validity of the control we are validating. 'Value' contains the value of the control being validated - in our case the contents of the txtSecondDate TextBox control. Instead of having to refer to txtSecondDate.Text.Trim() for example, we can just refer to args.Value. In this example, I've used the approach of assuming the second date is going to valid by, in the second line, setting args.IsValid = True. In subsequent comparisons to the current date and to the First Date textbox I set args.IsValid = False if args.Value falls outside the expected value range. valSecondDate.ErrorMessage is also set appropriately if a validation test is failed. If all tests are passed successfully, args.IsValid remains True and the lblMessage.Text is set to "-- Page Is Valid --".

Imports System.Web.UI.WebControls
Imports System.DateTime
Imports Microsoft.VisualBasic

Public Class CustomValidator
  Inherits System.Web.UI.Page

  Protected lblMessage As Label
  Protected txtFirstDate As TextBox
  Protected valSecondDate As System.Web.UI.WebControls.CustomValidator

  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

  End Sub

  Sub Validate_Date(sender As Object, args As ServerValidateEventArgs)
    lblMessage.Text = ""
    args.IsValid = True
    'Check for Valid Date
    If Not IsDate(args.Value) Then
      args.IsValid = False
      valSecondDate.ErrorMessage = "Second Date is not Valid!"
      Exit Sub
    End If
    'Check For Earlier Than First Date
    Dim dateFirst As System.DateTime
    Dim dateSecond As System.DateTime
    dateFirst = txtFirstDate.Text.Trim()
    dateSecond = args.Value
    If dateSecond < dateFirst Then
      args.IsValid = False
      valSecondDate.ErrorMessage = "Second Date must be equal to or later than First Date!"
      Exit Sub
    End If
    'Check for Later Than Today
    Dim dateNow as System.DateTime = Now()
    If dateSecond > dateNow Then
      args.IsValid = False
      valSecondDate.ErrorMessage = "Second Date must be equal to or earler than Today!"
      Exit Sub
    End If
    lblMessage.Text = "-- Page Is Valid --"
  End Sub
End Class

At the beginning of the article I mentioned that we would see a couple of quirks of the CustomValidator. One of these is that the CustomValidator event does not fire if the validated control is empty. In other words, if you run this example program below, and just click the Submit button without entering a Second Date, nothing happens. That is correct behavior since the Second Date is not a required field. The page is valid. But notice that our -- Page Is Valid -- message does not show up. That is because the Validate_Date method was never processed. You can add a Response.Write within Validate_Date, or create a breakpoint in debug mode, and they will not be processed if you do not enter a value in Second Date. If Second Date was a required field we could not just add code to the CustomValidator to check to make sure a value exists. If a value does not exist our code will not be executed. We would have to add a RequiredFieldValidator control to the page in addition to the CustomValidator (you can add as many validation controls as you need to any control on the page).

The other quirk about custom validation is not obvious from our example program. The ValidationSummary Control has a ShowMessageBox property which when set to True will pop up a message box containing all of the page validation errors. That makes it even more obvious to the user that there are errors. ShowMessageBox works perfectly for all other validation controls, but not for the CustomValidator. Errors from custom validators simply will not show up in the message box. I suppose this has something to do with the fact that the custom validation is happening server-side rather than client-side, but it would be nice if custom errors could be included in the message box.

Well, I hope you have learned that CustomValidator controls can be a powerful ally and are not really very hard to use. They allow you to program your way through even the most complicated validation scenarios.

You may download the code here.