Link to home
Start Free TrialLog in
Avatar of mghtbgiant
mghtbgiant

asked on

Custom Validators in congruence with CompareValidator (or RangeValidator) not functioning properly

To begin with, I am working under a very unique architecture. We are in a plug-in ascx based paradigm. Also, this is only a problem under non-IE browsers (namely Netscape 6+ for me) as all client-side validating works fine in IE. The problem is on the server-side.

Anyway, I have a UserControl (.ascx) for a date dange. It has two other ascx objects that wrap asp:textboxes. Both objects have their own range validator. So, we have this:

      txtStartDate.hdr.Text = mLingual.FromDate
      txtStartDate.Required = True
      txtStartDate.ran.Enabled = True
      txtStartDate.ran.ErrorMessage = mLingual.InvalidDate
      txtStartDate.ran.Type = ValidationDataType.Date
      txtStartDate.ran.MinimumValue = "01/01/1980"
      txtStartDate.ran.MaximumValue = "06/06/2079"

for both start and end dates. Now, in the encompassing ascx file there are two custom validators defined below:

<asp:customvalidator id="valDates" display="dynamic" onservervalidate="ServerValidationDates" cssclass="VALIDATOR" runat="server" />
<asp:customvalidator id="valRange" display="dynamic" onservervalidate="ServerValidationRange" cssclass="VALIDATOR" runat="server" />

In the code-behind, we have:

      '''date validation (fromdate greater than to date)
      valDates.ClientValidationFunction = "validateDates"
      valDates.ErrorMessage = mLingual.InvalidDateRange

      '''date range validation
      Session("FTFormatString") = common.MLDateFormatStr(Session("gLocaleID"))
      Session("FTRangeOf") = gRangeOf
      Session("FTSpan") = gSpan
      Dim denom As String
      Select Case gRangeOf.ToLower
        Case "m"
          denom = mLingual.Months
          dateInterval = dateInterval.Month
        Case "d"
          denom = mLingual.Days
          dateInterval = dateInterval.Day
        Case "y"
          denom = mLingual.Years
          dateInterval = dateInterval.Year
      End Select
      valRange.ClientValidationFunction = "validateRange"
      valRange.ErrorMessage = String.Format("{0} {1} {2}", mLingual.RangeExceeds, gSpan, denom)
      valDates.Enabled = bValidate
      valRange.Enabled = bValidate

      trVal.Visible = bValidate

where bValidate is controlled through a property. mLingual is just a class that holds our multilingual text exposed by properties.

One we go.. long story short, everything works, sometimes. Any time you input a plain invalid date into one of the fields, you post-back and are given the error "Invalid Date" which is expected. But, now if you enter a valid date, but and invalid range, for example start: "11/25/2003" end: "11/24/2003", the server-side methods set in OnServerValidate DO NOT RUN. This behavior happens with both RangeValidators used on the individual textboxes and CompareValidators with the type set to Date.

Given the plug-in architecture I have run in to many many event order issues. This, however, I am 99% confident has nothing to do with event order. You can do straight repetitive post-backs and as soon as you enter an invalid date (that fails the Compare/RangeValidator) the CustomValidator OnServerValidate methods DO NOT RUN.

To recap, the OnServerValidate functions run fine, UNTIL you input a bad date. Go to the page first time, run it with "11/25/2003" - "11/24/2003" you will get Invalid Date Range (because I have defined it as such). Now enter "asdfasdf" - "11/24/2003" you will get "Invalid Date" which should be expected. Now, when you rerun the first iteration "11/25/2003" - "11/24/2003" the OnServerValidate methods DO NOT RUN.

any help would be GREATLY appreciated. Thank you very much in advance.
Avatar of zrh
zrh

You may have just left this out, but you might need to add the ControlToValidate="YourTextBoxControl" attribute to your validators.  I know you can leave that out for custom controls, but don't know if that is how you have it implemented.  May be something to look at though.

I'll keep thinking about it.
ZRH


Avatar of mghtbgiant

ASKER

Thank you for your reply zrh, however, since the validation functions that are running, are not bound to one of the boxes, they run for both. The two functions that are bound to the CustomValidators are:

1.) One validates that date1 isn't greater than date2
2.) The other validates that the date range doesn't exceed the predefined range.

But, like you mentioned, ControlToValidate is not necessary for CustomValidators and that is how I have them implemented.

Thanks again for your input.
To preface, I apologize for the long post. I want to be as absolutely thorough as possible.

I have created a version of the malfunctioning code outside of my work's architecture and it is still behaving improperly. Create a new ASP.NET WebProject in VisualStudio and paste the below code for your WebForm1.aspx and code-behind. The date range accepted is 10 years. Acceptable dates in each field are from 1/1/1900 - 6/6/2079. IE works great because it never posts. Non-IE (namely Netscape 6+) is not working correctly. You can make the custom validators fail all day long and everything works fine when you then pass a valid range, then attempt another incorrect one. However, if one of the built-in validators (requiredField/range) fails, the page fails as expected.. then fix it, it works, now make a customValidator fail again (ie 01/01/2003 - 01/01/2030), it posts successfully, the routines don't even run. It's as if there is some global end-all-be-all variable (session or something) that only is set in non-IE that, when an inherent validator fails, it bombs, but then when it's fixed, it sets the flag that everything is okay. Page.Validate() is supposedly running, and coming back as true without running my OnServerValidate methods. I have no clue, that's why I'm here. The OnServerValidate Subs don't even run after you fail a built-in validator and then pass it. Period. They do not run regardless of validity of the customValidators.

So, here is a step-by-step procedure to test this behavior.. well assume the project is running in debug mode and netscape is open. put break points on the two ServerValidate methods.. also note, that when they run, they run two times each.

1.) enter 01/01/2003 - 01/20/2003
2.) submit
expected result, success
actual result, success
3.) replace 01/01/2003 with 01/21/2001
4.) submit
expected result, failed (invalid date range - start date greater than end date)
actual result, failed
5.) replace 01/21/2003 with 01/01/2002
6.) submit
expected result, success
actual result, success
7.) replace 01/20/2003 with 01/20/2050
8.) submit
expected result, failed (range exceeds 10 years)
actual result, failed
9.) replace 01/20/2050 with 01/20/2003
10.) submit
expected result, success
actual result, success
11.) blank out "EndDate"
12.) submit
expected result, failed (endDate required)
actual result, failed
13.) replace blank "EndDate" with 01/01/2030
14.) submit
expected result, failed (range exceeds 10 years)
actual result, success!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

This behavior can be achieved in fewer steps, however, I wanted to illustrate that the CustomValidators are working fine. Also, AS A VERY BIG NOTE, I just turned off all Client-side functionality in IE and got THE SAME BEHAVIOR. Remove ClientValidationFunction on the CustomValidators and change EnableClientScript on the Required/RangeValidators to "False" and you will get the absolute same behavior. I'm really hoping this is my problem, but I really don't think it is. I'm not a proud man and would gladly walk away giving someone with keen eyesight the 500 point bounty I put on this problem. If it's not my problem, maybe someone has a workaround because I also don't feel like paying Microsoft for them to fix their own problem.

Anyway, here is my code:

<!-- BEGIN WebForm1.aspx--------------------------------------------- -->
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" Inherits="ValidatorTest.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>WebForm1</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">

    <script language="javascript">
      var validRange = false;
      function validateDates(source, args) {
        if (!document.getElementById("txtStartDate").value || !document.getElementById("txtEndDate").value) {
          validRange = false;
          return;
        }
        var startDate = new Date(document.getElementById("txtStartDate").value);
        var endDate = new Date(document.getElementById("txtEndDate").value);
        if (startDate == 'NaN' || endDate == 'NaN') {
          validRange = false;
          return;
        }
        validRange = (startDate > endDate) ? false : true;
        args.IsValid = validRange;
      }

      function validateRange(source, args) {
        if (validRange) {
          var startDate = document.getElementById("txtStartDate").value;
          var endDate = document.getElementById("txtEndDate").value;
          <% = "var formatString = '" & Session("FormatString") & "';" %>
          <% = "var rangeOf = '" & Session("RangeOf") & "';" %>
          <% = "var rangeSpan = '" & Session("RangeSpan") & "';" %>
          args.IsValid = (!checkDateRange(startDate, endDate, formatString, rangeOf, rangeSpan)) ? false : true;
        }
      }

      function checkDateRange(startDate, endDate, formatString, rangeOf, numRange) {
        var formatIndexes = formatString.split('/');
        var mIndex = dIndex = yIndex = 0;
        for (var i = 0; i < formatIndexes.length; i++) {
          switch (formatIndexes[i].charAt(0).toLowerCase()) {
            case 'm':
              mIndex = i;
              break;
            case 'd':
              dIndex = i;
              break;
            case 'y':
              yIndex = i;
              break;
          }
        }

        if (startDate.indexOf('/') != -1) {
          var aryStartDate = startDate.split('/');
        } else if (startDate.indexOf('-') != -1) {
          var aryStartDate = startDate.split('-');
        } else if (startDate.indexOf('.') != -1) {
          var aryStartDate = startDate.split('.');
        }
        var mStartDate = Number(aryStartDate[mIndex]);
        var dStartDate = Number(aryStartDate[dIndex]);
        var yStartDate = Number(aryStartDate[yIndex]);
            var date1 = new Date(String(mStartDate + '/' + dStartDate + '/' + yStartDate));

        if (endDate.indexOf('/') != -1) {
          var aryEndDate = endDate.split('/');
        } else if (endDate.indexOf('-') != -1) {
          var aryEndDate = endDate.split('-');
        } else if (endDate.indexOf('.') != -1) {
          var aryEndDate = endDate.split('.');
        }
        var mEndDate = Number(aryEndDate[mIndex]);
        var dEndDate = Number(aryEndDate[dIndex]);
        var yEndDate = Number(aryEndDate[yIndex]);
            var date2 = new Date(String(mEndDate + '/' + dEndDate + '/' + yEndDate));

            var fromDateArray = startDate.split('/');
            var month = Number(fromDateArray[mIndex]);
            var day = Number(fromDateArray[dIndex]);
            var year = Number(fromDateArray[yIndex]);

        var mRange = 0, dRange = 0, yRange = 0;
        switch (rangeOf.toLowerCase()) {
          case 'y':
            yRange = Number(numRange);
            break;
          case 'm':
            mRange = Number(numRange);
            break;
          case 'd':
            dRange = Number(numRange);
            break;
        }

            var maxDate = new Date(String((month + mRange) + '/' + (day + dRange) + '/' + (year + yRange)));
        if (maxDate >= date2) { return true;
        } else { return false; }

        return true;
      }
    </script>
  </head>
  <body MS_POSITIONING="GridLayout">
    <form id="Form1" method="post" runat="server">
      <table cellspacing="0" cellpadding="0" border="0">
        <tr>
          <td><span style="font-size: 10pt; font-weight: bold;">Start Date</span></td>
          <td><span style="font-size: 10pt; font-weight: bold;">End Date</span></td>
        </tr>
        <tr>
          <td><asp:TextBox ID="txtStartDate" Columns="11" MaxLength="10" Runat="server" /></td>
          <td><asp:TextBox ID="txtEndDate" Columns="11" MaxLength="10" Runat="server" /></td>
          <td><asp:Label ID="lblPost" Visible="False" Font-Bold="True" Runat="server" /></td>
        </tr>
        <tr>
          <td>
            <asp:RequiredFieldValidator ID="reqStartDate" ControlToValidate="txtStartDate" EnableClientScript="True" Display="Dynamic" ErrorMessage="Required" Runat="server" />
            <asp:RangeValidator ID="ranStartDate" ControlToValidate="txtStartDate" EnableClientScript="True" Display="Dynamic" ErrorMessage="Invalid Date" Runat="server" />
            &nbsp;
          </td>
          <td>
            <asp:RequiredFieldValidator ID="reqEndDate" ControlToValidate="txtEndDate" EnableClientScript="True" Display="Dynamic" ErrorMessage="Required" Runat="server" />
            <asp:RangeValidator ID="ranEndDate" ControlToValidate="txtEndDate" EnableClientScript="True" Display="Dynamic" ErrorMessage="Invalid Date" Runat="server" />
            &nbsp;
          </td>
        </tr>
        <tr id="trCustom" visible="false" runat="server">
          <td colspan="2" align="center">
            <asp:CustomValidator ID="valDates" Display="Dynamic" ClientValidationFunction="validateDates" OnServerValidate="ServerValidateDates" Runat="server" />
            <asp:CustomValidator ID="valRange" Display="Dynamic" ClientValidationFunction="validateRange" OnServerValidate="ServerValidateRange" Runat="server" />
            &nbsp;
          </td>
        </tr>
      </table>
      <asp:Button ID="btnSubmit" Text="Submit" Runat="server" />
    </form>
  </body>
</html>
<!-- END WebForm1.aspx----------------------------------------------- -->

'BEGIN WebForm1.aspx.vb-----------------------------------------
Public Class WebForm1
    Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

    'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    End Sub

    'NOTE: The following placeholder declaration is required by the Web Form Designer.
    'Do not delete or move it.
    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    End Sub

#End Region

  Protected txtStartDate As TextBox
  Protected txtEndDate As TextBox
  Protected reqStartDate As RequiredFieldValidator
  Protected reqEndDate As RequiredFieldValidator
  Protected ranStartDate As RangeValidator
  Protected ranEndDate As RangeValidator
  Protected valDates As CustomValidator
  Protected valRange As CustomValidator
  Protected trCustom As HtmlTableRow

  Protected btnSubmit As Button
  Protected lblPost As Label

  Private bError As Boolean = False
  Private bCustomValidate = True
  Private dateInterval As Microsoft.VisualBasic.DateInterval
  Private dateFormat As String = "MM/dd/yyyy"
  Private rangeOf As String = "y"
  Private rangeSpan As Integer = 10

  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    '''===================BEGIN VALIDATORS=======================
    'requiredFieldValidator
    reqStartDate.Enabled = True
    reqEndDate.Enabled = True

    'rangeValidator
    '  startDate
    ranStartDate.Enabled = True
    ranStartDate.MinimumValue = "01/01/1900"
    ranStartDate.MaximumValue = "06/06/2079"
    ranStartDate.Type = ValidationDataType.Date
    '  endDate
    ranEndDate.Enabled = True
    ranEndDate.MinimumValue = "01/01/1900"
    ranEndDate.MaximumValue = "06/06/2079"
    ranEndDate.Type = ValidationDataType.Date

    'customValidator
    trCustom.Visible = bCustomValidate
    '  startDate cant be greater than endDate
    valDates.Enabled = bCustomValidate
    valDates.ErrorMessage = "Invalid date range"
    '  dateRange
    Session("FormatString") = dateFormat
    Session("RangeOf") = rangeOf
    Session("RangeSpan") = rangeSpan
    valRange.Enabled = bCustomValidate
    Dim denom As String
    Select Case rangeOf.ToLower
      Case "m"
        denom = "Month(s)"
        dateInterval = dateInterval.Month
      Case "d"
        denom = "Day(s)"
        dateInterval = dateInterval.Day
      Case "y"
        denom = "Year(s)"
        dateInterval = dateInterval.Year
    End Select
    valRange.ErrorMessage = String.Format("Range exceeds {0} {1}", rangeSpan, denom)
    '''====================END VALIDATORS========================
  End Sub

  Protected Sub ServerValidateDates(ByVal source As Object, ByVal args As ServerValidateEventArgs)
    If (Not IsDate(txtStartDate.Text) Or Not IsDate(txtEndDate.Text)) Then
      args.IsValid = False
      bError = True
      Exit Sub
    End If

    args.IsValid = IIf(CType(txtStartDate.Text, Date) > CType(txtEndDate.Text, Date), False, True)
  End Sub

  Protected Sub ServerValidateRange(ByVal source As Object, ByVal args As ServerValidateEventArgs)
    If (Not IsDate(txtStartDate.Text) Or Not IsDate(txtEndDate.Text)) Then
      args.IsValid = False
      bError = True
      Exit Sub
    End If

    args.IsValid = IIf(DateDiff(dateInterval, CType(txtStartDate.Text, Date), CType(txtEndDate.Text, Date)) <= rangeSpan, True, False)
  End Sub

  Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
    If (bError Or Not ranStartDate.IsValid Or Not ranEndDate.IsValid) Then
      valDates.Visible = False
      valRange.Visible = False
    End If

    If (IsPostBack) Then
      Page.Validate()
      lblPost.Text = IIf(Page.IsValid, "Success", "Failed")
      lblPost.Visible = IIf(IsPostBack, True, False)
    End If
  End Sub
End Class
'END WebForm1.aspx.vb-------------------------------------------
Hi, your problem was in the order you had things.  One thing to remember, the validation is done before the Page_Load event is called.  So basically I moved everything from Page_Load to Page_Init (actually I put it in IntitializedComponents, but same difference), and everything from Page_PreRender to Page_Load...  Also, I assume from your posted code that you want to have the range invalid if a field is blank. Here is your code again, the aspx page has been left alone, only the .vb file changed:

Public Class WebForm1
    Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

    'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        '''===================BEGIN VALIDATORS=======================
        'requiredFieldValidator
        reqStartDate.Enabled = True
        reqEndDate.Enabled = True

        'rangeValidator
        '  startDate
        ranStartDate.Enabled = True
        ranStartDate.MinimumValue = "01/01/1900"
        ranStartDate.MaximumValue = "06/06/2079"
        ranStartDate.Type = ValidationDataType.Date
        '  endDate
        ranEndDate.Enabled = True
        ranEndDate.MinimumValue = "01/01/1900"
        ranEndDate.MaximumValue = "06/06/2079"
        ranEndDate.Type = ValidationDataType.Date

        'customValidator
        trCustom.Visible = bCustomValidate
        '  startDate cant be greater than endDate
        valDates.Enabled = bCustomValidate
        valDates.ErrorMessage = "Invalid date range"
        '  dateRange
        Session("FormatString") = dateFormat
        Session("RangeOf") = rangeOf
        Session("RangeSpan") = rangeSpan
        valRange.Enabled = bCustomValidate
        Dim denom As String
        Select Case rangeOf.ToLower
            Case "m"
                denom = "Month(s)"
                dateInterval = dateInterval.Month
            Case "d"
                denom = "Day(s)"
                dateInterval = dateInterval.Day
            Case "y"
                denom = "Year(s)"
                dateInterval = dateInterval.Year
        End Select
        valRange.ErrorMessage = String.Format("Range exceeds {0} {1}", rangeSpan, denom)
        '''====================END VALIDATORS========================
    End Sub

    'NOTE: The following placeholder declaration is required by the Web Form Designer.
    'Do not delete or move it.
    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    End Sub

#End Region

    Protected txtStartDate As TextBox
    Protected txtEndDate As TextBox
    Protected reqStartDate As RequiredFieldValidator
    Protected reqEndDate As RequiredFieldValidator
    Protected ranStartDate As RangeValidator
    Protected ranEndDate As RangeValidator
    Protected valDates As CustomValidator
    Protected valRange As CustomValidator
    Protected trCustom As HtmlTableRow

    Protected btnSubmit As Button
    Protected lblPost As Label

    Private bError As Boolean = False
    Private bCustomValidate = True
    Private dateInterval As Microsoft.VisualBasic.DateInterval
    Private dateFormat As String = "MM/dd/yyyy"
    Private rangeOf As String = "y"
    Private rangeSpan As Integer = 10

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If (bError Or Not ranStartDate.IsValid Or Not ranEndDate.IsValid) Then
            valDates.Visible = False
            valRange.Visible = False
        End If

        If (IsPostBack) Then
            Page.Validate()
            lblPost.Text = IIf(Page.IsValid, "Success", "Failed")
            lblPost.Visible = IIf(IsPostBack, True, False)
        End If
    End Sub

    Protected Sub ServerValidateDates(ByVal source As Object, ByVal args As ServerValidateEventArgs)
        If (Not IsDate(txtStartDate.Text) Or Not IsDate(txtEndDate.Text)) Then
            args.IsValid = False
            bError = True
            Exit Sub
        End If

        args.IsValid = IIf(CType(txtStartDate.Text, Date) > CType(txtEndDate.Text, Date), False, True)
    End Sub

    Protected Sub ServerValidateRange(ByVal source As Object, ByVal args As ServerValidateEventArgs)
        If (Not IsDate(txtStartDate.Text) Or Not IsDate(txtEndDate.Text)) Then
            args.IsValid = False
            bError = True
            Exit Sub
        End If

        args.IsValid = IIf(DateDiff(dateInterval, CType(txtStartDate.Text, Date), CType(txtEndDate.Text, Date)) <= rangeSpan, True, False)
    End Sub
End Class

Hope that is what you wanted,
ZRH
ZRH,

Thanks again for your response. I am looking into some things. I do not want to show an error message for an invalid range when an invalid date has been entered but I can take care of that. One thing that is coming to mind, due to the sometimes overly frustrating nature of event firing, I don't know if moving the code to Page_Init will work as the variable bCustomValidate is controlled by a property. Basically, I have an .ascx UserControl that encapsulates this date range functionality and when instantiating this control in another page you say whether or not to validate, and then define the range. Anyway, I don't know (and highly doubt) that the property that influences bCustomValidate runs before Page_Init. I know the property will run before Page_Load, but I think Page_Init is the #1 mamma-jamma method.

I'll play with what you're suggesting, and also create me an .ascx control to check the event model. Anyway, I have some more to look at.

Thanks again, I'll post again when I get some things thrown together.
Seriously, this is frustrating. Your changes functioned properly. However, when I put things into the correct architecture, the same thing happens as before. The properties do indeed run before Page_Init so that isn't an issue. But, now with the right paradigm, the same thing is happening.

Here is the new code:

<!-- BEGIN WebForm1.aspx ---------------------------------------- -->
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" Inherits="ValidatorTest.WebForm1"%>
<%@ Register TagPrefix="ctrls" TagName="FromToDates" Src="FromToDates.ascx" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>WebForm1</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 MS_POSITIONING="GridLayout">
    <form id="Form1" method="post" runat="server">
      <table>
        <tr valign="top">
          <td><ctrls:FromToDates ID="FromToDates" CustomValidate="True" RangeOf="y" RangeSpan="10" Columns="11" MaxLength="10" Runat="server" /></td>
          <td><asp:Label ID="lblPost" Visible="False" Font-Bold="True" Runat="server" /></td>
        </tr>
      </table>
      <asp:Button ID="btnSubmit" Text="Submit" Runat="server" />
    </form>
  </body>
</html>
<!-- END WebForm1.aspx ------------------------------------------ -->

'BEGIN WebForm1.aspx.vb --------------------------------------- -->
Public Class WebForm1
  Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
  End Sub

  'NOTE: The following placeholder declaration is required by the Web Form Designer.
  'Do not delete or move it.
  Private designerPlaceholderDeclaration As System.Object

  Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub

#End Region

  Protected lblPost As Label
  Private bPageValid As Boolean

  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    If (IsPostBack) Then
      Page.Validate()
      bPageValid = Page.IsValid
    End If
  End Sub

  Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
    If (IsPostBack) Then
      lblPost.Text = IIf(bPageValid, "Success", "Failed")
    End If
    lblPost.Visible = IIf(IsPostBack, True, False)
  End Sub
End Class
'END WebForm1.aspx.vb ------------------------------------------ -->

<!-- BEGIN FromToDates.ascx ---------------------------------------- -->
<%@ Control Language="vb" AutoEventWireup="false" Codebehind="FromToDates.ascx.vb" Inherits="ValidatorTest.FromToDates" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<script language="javascript">
  var validRange = false;
  function validateDates(source, args) {
    if (!document.getElementById(findControl("txtFromDate", "INPUT")).value || !document.getElementById(findControl("txtToDate", "INPUT")).value) {
      validRange = false;
      return;
    }
    var startDate = new Date(document.getElementById(findControl("txtFromDate", "INPUT")).value);
    var endDate = new Date(document.getElementById(findControl("txtToDate", "INPUT")).value);
    if (startDate == 'NaN' || endDate == 'NaN') {
      validRange = false;
      return;
    }
    validRange = (startDate > endDate) ? false : true;
    args.IsValid = validRange;
  }

  function validateRange(source, args) {
    if (validRange) {
      var startDate = document.getElementById(findControl("txtFromDate", "INPUT")).value;
      var endDate = document.getElementById(findControl("txtToDate", "INPUT")).value;
      <% = "var formatString = '" & Session("FormatString") & "';" %>
      <% = "var rangeOf = '" & Session("RangeOf") & "';" %>
      <% = "var rangeSpan = '" & Session("RangeSpan") & "';" %>
      args.IsValid = (!checkDateRange(startDate, endDate, formatString, rangeOf, rangeSpan)) ? false : true;
    }
  }

  function checkDateRange(startDate, endDate, formatString, rangeOf, numRange) {
    var formatIndexes = formatString.split('/');
    var mIndex = dIndex = yIndex = 0;
    for (var i = 0; i < formatIndexes.length; i++) {
      switch (formatIndexes[i].charAt(0).toLowerCase()) {
        case 'm':
          mIndex = i;
          break;
        case 'd':
          dIndex = i;
          break;
        case 'y':
          yIndex = i;
          break;
      }
    }

    if (startDate.indexOf('/') != -1) {
      var aryStartDate = startDate.split('/');
    } else if (startDate.indexOf('-') != -1) {
      var aryStartDate = startDate.split('-');
    } else if (startDate.indexOf('.') != -1) {
      var aryStartDate = startDate.split('.');
    }
    var mStartDate = Number(aryStartDate[mIndex]);
    var dStartDate = Number(aryStartDate[dIndex]);
    var yStartDate = Number(aryStartDate[yIndex]);
        var date1 = new Date(String(mStartDate + '/' + dStartDate + '/' + yStartDate));

    if (endDate.indexOf('/') != -1) {
      var aryEndDate = endDate.split('/');
    } else if (endDate.indexOf('-') != -1) {
      var aryEndDate = endDate.split('-');
    } else if (endDate.indexOf('.') != -1) {
      var aryEndDate = endDate.split('.');
    }
    var mEndDate = Number(aryEndDate[mIndex]);
    var dEndDate = Number(aryEndDate[dIndex]);
    var yEndDate = Number(aryEndDate[yIndex]);
        var date2 = new Date(String(mEndDate + '/' + dEndDate + '/' + yEndDate));

        var fromDateArray = startDate.split('/');
        var month = Number(fromDateArray[mIndex]);
        var day = Number(fromDateArray[dIndex]);
        var year = Number(fromDateArray[yIndex]);

    var mRange = 0, dRange = 0, yRange = 0;
    switch (rangeOf.toLowerCase()) {
      case 'y':
        yRange = Number(numRange);
        break;
      case 'm':
        mRange = Number(numRange);
        break;
      case 'd':
        dRange = Number(numRange);
        break;
    }

        var maxDate = new Date(String((month + mRange) + '/' + (day + dRange) + '/' + (year + yRange)));
    if (maxDate >= date2) { return true;
    } else { return false; }

    return true;
  }

  function findControl(ctlName, ctlType){
    var aryCtls = document.getElementsByTagName(ctlType);
    var ctlRegExp = new RegExp(ctlName + '$');

    for (var i = 0; i < aryCtls.length; i++) {
      if (aryCtls[i].id.match(ctlRegExp)) {
        return aryCtls[i].id;
      }
    }
  }
</script>
<table cellspacing="0" cellpadding="0" border="0">
  <tr>
    <td><span style="font-size: 10pt; font-weight: bold;">From Date</span></td>
    <td><span style="font-size: 10pt; font-weight: bold;">To Date</span></td>
  </tr>
  <tr>
    <td><asp:TextBox ID="txtFromDate" Runat="server" /></td>
    <td><asp:TextBox ID="txtToDate" Runat="server" /></td>
  </tr>
  <tr>
    <td>
      <asp:RequiredFieldValidator ID="reqFromDate" ControlToValidate="txtFromDate" EnableClientScript="False" Display="Dynamic" ErrorMessage="Required" Runat="server" />
      <asp:RangeValidator ID="ranFromDate" ControlToValidate="txtFromDate" EnableClientScript="False" Display="Dynamic" ErrorMessage="Invalid Date" Runat="server" />
      &nbsp;
    </td>
    <td>
      <asp:RequiredFieldValidator ID="reqToDate" ControlToValidate="txtToDate" EnableClientScript="False" Display="Dynamic" ErrorMessage="Required" Runat="server" />
      <asp:RangeValidator ID="ranToDate" ControlToValidate="txtToDate" EnableClientScript="False" Display="Dynamic" ErrorMessage="Invalid Date" Runat="server" />
      &nbsp;
    </td>
  </tr>
  <tr id="trCustom" visible="false" runat="server">
    <td colspan="2" align="center">
      <asp:CustomValidator ID="valDates" Display="Dynamic" OnServerValidate="ServerValidateDates" Runat="server" />
      <asp:CustomValidator ID="valRange" Display="Dynamic" OnServerValidate="ServerValidateRange" Runat="server" />
      &nbsp;
    </td>
  </tr>
</table>
<!-- END FromToDates.ascx ------------------------------------------ -->

'BEGIN FromToDates.ascx.vb ------------------------------------- -->
Public Class FromToDates
  Inherits System.Web.UI.UserControl

#Region " Global "
  Protected txtFromDate As TextBox
  Protected txtToDate As TextBox
  Protected reqFromDate As RequiredFieldValidator
  Protected reqToDate As RequiredFieldValidator
  Protected ranFromDate As RangeValidator
  Protected ranToDate As RangeValidator
  Protected valDates As CustomValidator
  Protected valRange As CustomValidator
  Protected trCustom As HtmlTableRow

  Protected btnSubmit As Button

  Private bError As Boolean = False
  Private bCustomValidate As Boolean ' = True
  Private dateInterval As Microsoft.VisualBasic.DateInterval
  Private dateFormat As String = "MM/dd/yyyy"
  Private sRangeOf As String ' = "y"
  Private iRangeSpan As Integer ' = 10
#End Region

#Region " Properties "
  Public WriteOnly Property Columns() As Integer
    Set(ByVal Value As Integer)
      txtFromDate.Columns = Value
      txtToDate.Columns = Value
    End Set
  End Property

  Public WriteOnly Property MaxLength() As Integer
    Set(ByVal Value As Integer)
      txtFromDate.MaxLength = Value
      txtToDate.MaxLength = Value
    End Set
  End Property

  Public Property CustomValidate() As Boolean
    Get
      Return bCustomValidate
    End Get
    Set(ByVal Value As Boolean)
      bCustomValidate = Value
    End Set
  End Property

  Public Property RangeOf() As String
    Get
      Return sRangeOf
    End Get
    Set(ByVal Value As String)
      sRangeOf = Value
    End Set
  End Property

  Public Property RangeSpan() As Integer
    Get
      Return iRangeSpan
    End Get
    Set(ByVal Value As Integer)
      iRangeSpan = Value
    End Set
  End Property
#End Region

#Region " Web Form Designer Generated Code "
  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    '''===================BEGIN VALIDATORS=======================
    'requiredFieldValidator
    reqFromDate.Enabled = True
    reqToDate.Enabled = True

    'rangeValidator
    '  startDate
    ranFromDate.Enabled = True
    ranFromDate.MinimumValue = "01/01/1900"
    ranFromDate.MaximumValue = "06/06/2079"
    ranFromDate.Type = ValidationDataType.Date
    '  endDate
    ranToDate.Enabled = True
    ranToDate.MinimumValue = "01/01/1900"
    ranToDate.MaximumValue = "06/06/2079"
    ranToDate.Type = ValidationDataType.Date

    'customValidator
    trCustom.Visible = bCustomValidate
    '  fromDate cant be greater than toDate
    valDates.Enabled = bCustomValidate
    valDates.ErrorMessage = "Invalid date range"
    '  dateRange
    Session("FormatString") = dateFormat
    Session("RangeOf") = sRangeOf
    Session("RangeSpan") = iRangeSpan
    valRange.Enabled = bCustomValidate
    Dim denom As String
    Select Case RangeOf.ToLower
      Case "m"
        denom = "Month(s)"
        dateInterval = dateInterval.Month
      Case "d"
        denom = "Day(s)"
        dateInterval = dateInterval.Day
      Case "y"
        denom = "Year(s)"
        dateInterval = dateInterval.Year
    End Select
    valRange.ErrorMessage = String.Format("Range exceeds {0} {1}", iRangeSpan, denom)
    '''====================END VALIDATORS========================
  End Sub

  'NOTE: The following placeholder declaration is required by the Web Form Designer.
  'Do not delete or move it.
  Private designerPlaceholderDeclaration As System.Object

  Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub
#End Region

#Region " Methods "
  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    '
  End Sub

  Protected Sub ServerValidateDates(ByVal source As Object, ByVal args As ServerValidateEventArgs)
    If (Not IsDate(txtFromDate.Text) Or Not IsDate(txtToDate.Text)) Then
      args.IsValid = False
      bError = True
      Exit Sub
    End If

    args.IsValid = IIf(CType(txtFromDate.Text, Date) > CType(txtToDate.Text, Date), False, True)
  End Sub

  Protected Sub ServerValidateRange(ByVal source As Object, ByVal args As ServerValidateEventArgs)
    If (Not IsDate(txtFromDate.Text) Or Not IsDate(txtToDate.Text)) Then
      args.IsValid = False
      bError = True
      Exit Sub
    End If

    args.IsValid = IIf(DateDiff(dateInterval, CType(txtFromDate.Text, Date), CType(txtToDate.Text, Date)) <= RangeSpan, True, False)
  End Sub

  Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
    If (bError Or Not ranFromDate.IsValid Or Not ranToDate.IsValid) Then
      valDates.Visible = False
      valRange.Visible = False
    End If
  End Sub
#End Region

End Class
'END FromToDates.ascx.vb --------------------------------------- -->

=============================================================

What I really don't understand is why it works fine until I fail one of the built-in validators. Page.Validate is apparently working fine, it runs, raises the ServerValidate event for the CustomValidator like it's supposed to.. but as soon as you fail a pre-defined validator it loses its mind.

Anyway, that's what I'm working with now. So, I think if we can get this last kink, we got ourselves a solution, and you get yourself 500 points. I really appreciate your help.
Avatar of Bob Learned
No comment has been added lately, so it's time to clean up this TA.
I will leave the following recommendation for this question in the Cleanup topic area:

PAQ with points refunded

Please leave any comments here within the next seven days.
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

TheLearnedOne
EE Cleanup Volunteer
ASKER CERTIFIED SOLUTION
Avatar of Computer101
Computer101
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial