Click here to Skip to main content
Click here to Skip to main content

ASP.NET validation in groups

, 12 Sep 2001
Rate this:
Please Sign up or sign in to vote.
How to circumvent the all-or-nothing validation model of ASP.NET
<!-- Download Links -->

The problem

The validation controls of ASP.NET offer a great deal of flexibility and comfort when you need to write data entry pages in your web application. They provide the ability to ensure valid data in the input fields in a rather declarative than procedural way. So far so good.

However, I came across the following problem during the development of a portal application. It uses user controls to customize the modules and the layout that make up the portal page. Now I had two modules with some input fields each which checked the validity with standard validation controls offered by ASP.NET. It all was fine until I configured the portal to display both modules at same time on the page. Every time I tried to enter (valid) data in one of the modules the other one complained about empty required fields. This behaviour is by design as validation occurs on the page-level (the Validators collection is a member of the Page object). However, it was not the intended behaviour as I wanted it.

While thinking about a solution for this, I realized that this is not an uncommon problem. Imagine a form that handles different payment methods. Depending on the selected method different fields should be mandatory while others remain empty. 

How validation works

In order to find a solution it's always good to understand how things work. According to MSDN a server round-trip includes the following steps:

  • Create page and control objects according to the aspx file
  • Recover state of the objects from viewstate
  • Update objects based on user input
  • Fire Page_Load event
  • Fire change notification events
  • Save object state in viewstate
  • Render HTML

Validation occurs between step 3 and step 4 in the case of a postback. This usually does make sense, as it ensures that the validation takes place before user code is executed as a response of an event. However it makes life a bit harder when controling the validation process itself.

The Solution

The first approach was to look for a built-in standard way of solving this, as it seemed a common problem to me. I didn't find one. After trying various approaches to tackle this, I came up with the solution below. The beauty of this approach is the fact that you don't have to use custom controls and don't have to change existing pages too much. It's all centered around two little single functions. However, it also has one big drawback: you have to disable client side validation for uplevel browsers.

Your aspx page looks like the following:

<%@ Page language="c#" Trace="true" clienttarget="downlevel" Codebehind="default.aspx.cs" 
    AutoEventWireup="false" Inherits="GroupValidator.TestForm" %>    

....some HTML here ....

<form id="Form1" method="post" runat="server">
<table border=1>
<tr><td>
    <asp:CheckBox id="Box1" Text="Validate Group 1" runat="server"/>
    
    <asp:Placeholder id="Group1" runat="server">
        <asp:TextBox id="Text1" runat="server" wordwrap=false 
                     height="70px" width="100" rows="15"></asp:TextBox>
        <asp:RequiredFieldValidator id="RequiredFieldValidator1"
                ControlToValidate="Text1"
                Display="Static"                          
                InitialValue="" Width="100%" runat="server">*
        </asp:RequiredFieldValidator>
        <asp:CustomValidator id="CustomValidator1" runat="server"
                controltovalidate="Text1"                 
                errormessage="ID is already in use." 
                OnServerValidate="CheckID" />
    </asp:Placeholder>
    
    </td></tr><tr><td>                
    
    <asp:CheckBox id="Box2" Text="Validate Group 2" runat="server"/>
    <asp:Placeholder id="Group2" runat="server">    
        ZIP Code: <asp:TextBox id="Text2" runat="server" wordwrap=false 
                               height="70px" width="100" rows="15"></asp:TextBox>
        <asp:RequiredFieldValidator id="RequiredFieldValidator2"
                ControlToValidate="Text2"
                Display="Static"                            
                InitialValue="" Width="100%" runat="server">*
        </asp:RequiredFieldValidator>   
        <asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server"
            ControlToValidate="Text2"
            ValidationExpression="^\d{5}$"
            Display="Static"
            Font-Name="verdana" 
            Font-Size="10pt">
            Zip code must be 5 numeric digits
        </asp:RegularExpressionValidator>
    </asp:Placeholder>
    
</td></tr>
</table>
<asp:Button id="Button1" text="Validate" OnClick="Button1Click" runat="server"/>
</form>

....some more HTML here ....

So what are we doing here? The first thing to note is the ClientTarget Page directive. It's set on "downlevel" to disable client-side validation scripts. The second thing are the two PlaceHolder tags that contain some input fields as well (and that's the important point) some validation controls. They define the validation groups. The placeholders here are just an example. If you use this technique in an user control you could also use the control itself as a grouping container.

What's next?

With the current aspx file all four validators would trigger on a postback event. Nothing new until now. In fact we don't care about it! Let's look at the Button1Click function:

public void Button1Click(Object Sender, EventArgs evt)
{            
    Trace.Write ("Entering Validation");
    DisableValidators();

    if(Box1.Checked == true)
    {
        Trace.Write ("Group 1 Validation");
        EnableValidators(Group1);
    }
    if(Box2.Checked == true)
    {
        Trace.Write ("Group 2 Validation");
        EnableValidators(Group2);
    }
    Page.Validate();
    
    if(Page.IsValid == true)
        Trace.Write("The page is valid");
    
}        
        

What we are doing here, is basically to re-run the validation process. Before we do this, we reset the status of the validation controls according to the selection. To define the group we need a container obejct - our placeholder. The actual work is done by these functions:

private void DisableValidators()
{
    Trace.Write ("Disabling all validator controls on page");
    foreach (BaseValidator bv in this.Validators)
        bv.Enabled = false;
}

private void EnableValidators(Control container)
{                        
    foreach (Control c in container.Controls)
    {                
        if(c is IValidator)
        {
            Trace.Write("Enabling " + c.ID);
            // Assumption here: 
            // every control that implements IValidator is derived from BaseValidator class
            ((BaseValidator)c).Enabled = true;    
        }
    }
}        
        

These two functions can be placed wherever they fit. So basically we are running the validation twice as we cannot change the default validation. The only way to circumvent this, is to disable the validators by default in the aspx page.

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

Share

About the Author

Christian Tratz
Web Developer
Germany Germany
If you are not living on the edge you are wasting space Wink | ;)

Comments and Discussions

 
GeneralYou don't need to write any codebehind to solve this Pinmemberxess999-May-09 16:53 
GeneralCustom class solution! PinsussGary Dmitriev21-Jul-05 16:11 
GeneralBetter way to disable validation to launch yours. Pinmemberemorales20-Jul-05 5:18 
GeneralDataBase Problem PinsussAnonymous4-Jun-05 0:12 
GeneralRe: DataBase Problem Pinmemberamitavabardhan4-Nov-05 6:35 
GeneralConvert to VB.NET issue PinmemberDgtHorse23-May-05 17:21 
GeneralRe: Convert to VB.NET issue PinmemberDgtHorse23-May-05 17:26 
GeneralValidation of duplicate records using MySQL Database PinsussAnonymous10-Sep-03 22:33 
GeneralTip Pinsussdaltonics2-May-03 14:49 
GeneralThat's not correct! Pinmemberspoimala19-Jul-02 2:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 13 Sep 2001
Article Copyright 2001 by Christian Tratz
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid