Click here to Skip to main content
15,867,308 members
Articles / Web Development / ASP.NET
Article

RequiredIfValidator - Extending from the BaseValidator class

Rate me:
Please Sign up or sign in to vote.
4.78/5 (22 votes)
12 Jan 20056 min read 129.5K   719   71   31
Have you ever had a field that was required, but only if another field was a certain value? Then, this validator is for you!

Screenshot of Validation

Introduction

When writing ASP.NET pages that need to be validated before submission, I find myself coming across controls that only need to be validated if another control contains a particular value. A classic example of this is an "If other, please specify" field. Typically, you'll have a combobox that contains a list of choices, and at the end of the list is an "Other" item. If the user chooses "Other", you'd like them to specify exactly what "Other" means.

My previous approach was to include a JavaScript method that would make sure the "please specify" field was not empty if the combobox had the "Other" item selected. I would wire up the method to the Form's onSubmit event. This way worked, but each time I did it, I had to manually hard-code the JavaScript method onto the page, changing it a little bit each time to account for control names. Not only was it inefficient, but also error-prone.

To fix the problem, I've created a new validation control that inherits directly from BaseValidator. The control takes my previous approach one step further to make this kind of validation extremely simple.

Background

I'm going to assume some familiarity with the ASP.NET validation controls since this isn't a tutorial on what they are or how they work, but rather an implementation of a custom one. With that said, let's get started.

Introducing the Control

My validator adds a couple of properties to the BaseValidator class. They are:

  • ControlToCompare
  • InitialValue
  • TriggerValue

The ControlToCompare works exactly like the CompareValidator.ControlToCompare property, while the InitialValue property works exactly like the RequiredFieldValidator.InitialValue property. This makes perfect sense since I'm doing a little bit of both types of validation with this control.

The TriggerValue is the only property that's entirely new. When validating, the value of this property will be compared to the value of the control specified by the ControlToCompare property. If the two are equal, it means that the control specified by the BaseValidator.ControlToValidate property is now required. If the values are different, then the BaseValidator.ControlToValidate control is not required.

This may be a little confusing all at once, but hopefully I'll clear it up by the end of the article.

Important Methods

For custom validators, the most important method that you'll be overriding is the EvaluateIsValid method. This method is called for server-side validation of the control. You return true if validation succeeds or false if it doesn't.

For the RequiredIfValidator's EvaluateIsValid, I need to do two things at most. First, I need to see if the control we're validating is required or not. I do this by calling a private method called CheckCompareCondition. The method looks like this:

C#
private bool CheckCompareCondition()
{
    bool isRequired = false;

    string compareValue = this.GetControlValidationValue(ControlToCompare);

    if (compareValue == TriggerValue)
    {
        isRequired = true;
    }

    return isRequired;
}

As you can see, I'm grabbing the validation value of the ControlToCompare control and comparing it to the TriggerValue. If there is a match, then I know that the ControlToValidate is now required. If that's the case, I then have to see if the value of the ControlToValidate is different from the InitialValue. My implementation to do this is exactly what the RequiredFieldValidator does for its EvaluateIsValid method:

C#
string controlValue;
controlValue = this.GetControlValidationValue(this.ControlToValidate);

if (controlValue == null)
{
    return true;
}
else
{
    return controlValue.Trim() != this.InitialValue.Trim();
}

Client-side validation is handled by injecting some JavaScript into the page by overriding the OnPreRender method. Basically, I inject the JavaScript by calling the Page's RegisterClientScriptBlock method if the script has not been registered yet. This is the JavaScript method that I inject:

JavaScript
function RequiredIfValidatorEvaluateIsValid(val)
{
    if (val.controltocompare != "") 
    {
        if (document.getElementById(val.controltocompare).value 
                                            == val.triggervalue) 
        {
            //use asp.net's method!
            return RequiredFieldValidatorEvaluateIsValid(val); 
        } 
        else 
        {
            return true;
        }
    } 
    else 
    {
        return true;
    }
}

Here, I'm doing basically the same thing as my server-side EvaluateIsValid method does. The only thing interesting is that I'm actually utilizing ASP.NET's WebUIValidation.js script to do the validation if the ControlToValidate ends up being required.

When looking at the JavaScript method, you may be wondering how I'm able to use val.triggervalue or val.controltocompare. This is where the final important method, AddAttributesToRender comes into play. This method is a virtual method inherited way back from the WebControl class. This method's job is to render any HTML attributes out to the opening tag that are necessary.

For my implementation, I do the following:

C#
base.AddAttributesToRender(writer);

if (this.RenderUplevel)
{
    //this attribute is needed by the RequiredFieldValidator's
    //validation method.
    writer.AddAttribute("initialvalue", InitialValue);

    //this is the method that asp.net calls when validating my
    //control on the client-side
    writer.AddAttribute("evaluationfunction",
                         "RequiredIfValidatorEvaluateIsValid");

    //attributes that my client-side method will use to validate itself.
    writer.AddAttribute("controltocompare",
           this.GetControlRenderID(ControlToCompare));
    writer.AddAttribute("triggervalue", TriggerValue);
}

Here, you can see I'm adding the attributes that my JavaScript method uses when validating on the client-side. The really important one is the evaluationfunction attribute. This method is called by the ValidatorValidate method located in Microsoft's WebUIValidation.js script. This is how my JavaScript method actually gets wired up by ASP.NET to be called when the page validates on the client.

One other thing worth mentioning is that this method is the reason why I didn't inherit my class from RequiredFieldValidator. Specifically, the call to base.AddAttributesToRender is what did it. The RequiredFieldValidator.AddAttributesToRender method adds the initialvalue and evaluationfunction attributes to the tag, and I wanted to add my own evaluationfunction attribute to the tag. The problem is that the attribute has now already been written, so I'd be adding it a second time with a different value. Both would end up being rendered to the page.

I hear you saying, "Well, then just don't call the base class AddAttributesToRender method!". Well, remember when I said that the method is inherited all the way back from WebControl? The WebControl class does quite a bit of important work in this method, and so does the BaseValidator. There's no reason I should painstakingly re-write all of that functionality in my own control, which could be error-prone.

Conclusion

Hopefully, this article has given you a closer look into the internals of a validation control (maybe closer than you wanted!), but most importantly, I hoped you've learned something new after reading this. At the very least, I hope you'll be able to put this control to good use.

Using the code

The source code is only one file, RequiredIfValidator.cs. To build the control, you can run the following from a command line:

csc /t:library RequiredIfValidator.cs

Once compiled, you can throw the DLL in your web application's bin directory. Then, when you want to use the control in one of your aspx pages, just include the following directive in your page:

ASP.NET
<%@ Register TagPrefix="BNewtz" Namespace="BNewtz.Controls" 
                      Assembly="RequiredIfValidator" %>

Using the demo

The demo is just a simple webpage that shows the functionality of the control. The demo contains the demo.aspx page as well as the compiled DLL of the control. Just throw the demo.aspx page in a virtual directory, and the DLL into the bin subdirectory of your application. Once that's done, just view demo.aspx in your web browser of choice.

Points of Interest

For those of you curious to know how I was able to know what was going on in Microsoft's own controls, the answer is Anakrino. Anakrino is a decompiler that converts MSIL back into C#. It's a great tool to uncover the inner workings of a class, especially if it's one that you're deriving from.

History

  • 11/16/2004 - Article posted.
  • 1/10/2005 - Bug fix: Thanks to Scott for pointing out that the control did not work correctly when inside of a container control implementing INamingContainer. I've adjusted the code within the AddAttributesToRender method and updated the article and downloads with the new version.

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


Written By
United States United States
Brian is a software developer at a manufacturing company in Ohio. He does mostly C# windows and web programming, with a small pinch of VB thrown in when necessary.

He is a moderator for the CSharpNET Yahoo group, which you can visit by going here:

http://groups.yahoo.com/group/CSharpNET/

Comments and Discussions

 
QuestionAnyone having trouble with this and JQuery conflicting? Pin
dschwarz19-Jan-10 12:10
dschwarz19-Jan-10 12:10 
Generalthe validator only triggered after other validator passed Pin
yanhui bian16-May-08 6:28
yanhui bian16-May-08 6:28 
GeneralRe: the validator only triggered after other validator passed Pin
yanhui bian16-May-08 11:16
yanhui bian16-May-08 11:16 
GeneralFirefox client side Pin
ztron30-Sep-07 16:06
ztron30-Sep-07 16:06 
Great control, just one thing...
For those of you trying to get the client side validator to work in firefox, check this out
http://aspadvice.com/blogs/joteke/archive/2006/08/26/Remember-expando-attributes-with-custom-controls.aspx
Replace
writer.AddAttribute("initialvalue", InitialValue);
with
Page.ClientScript.RegisterExpandoAttribute(this.ClientID, "initialvalue", InitialValue);
and similar for the other 3 writer.AddAttribute statements
GeneralVisual Web Developer 2005 Express Pin
SD0610-Aug-07 1:50
SD0610-Aug-07 1:50 
GeneralRe: Visual Web Developer 2005 Express Pin
Datac0re11-Aug-07 1:43
Datac0re11-Aug-07 1:43 
GeneralRe: Visual Web Developer 2005 Express Pin
SD0612-Aug-07 22:41
SD0612-Aug-07 22:41 
GeneralRe: Visual Web Developer 2005 Express Pin
SD0613-Aug-07 4:41
SD0613-Aug-07 4:41 
GeneralRe: Visual Web Developer 2005 Express Pin
SD0614-Aug-07 6:11
SD0614-Aug-07 6:11 
GeneralValidation of textbox on Checkbox.Checked==true or radio buttons selection Pin
winsum13-May-06 23:43
winsum13-May-06 23:43 
GeneralRe: Validation of textbox on Checkbox.Checked==true or radio buttons selection Pin
Glenn Holden21-May-06 11:04
Glenn Holden21-May-06 11:04 
GeneralRe: Validation of textbox on Checkbox.Checked==true or radio buttons selection Pin
newguy207-Jul-06 3:14
newguy207-Jul-06 3:14 
GeneralRe: Validation of textbox on Checkbox.Checked==true or radio buttons selection Pin
Nick Chadwick16-Aug-06 5:13
Nick Chadwick16-Aug-06 5:13 
NewsMultipleFieldsValidator for ASP.NET 2.0 Pin
Adam Tibi20-Mar-06 10:36
professionalAdam Tibi20-Mar-06 10:36 
GeneralRe: MultipleFieldsValidator for ASP.NET 2.0 Pin
Datac0re20-Mar-06 13:15
Datac0re20-Mar-06 13:15 
GeneralErrormessage Question Pin
26-Sep-05 4:44
suss26-Sep-05 4:44 
GeneralRe: Errormessage Question Pin
Datac0re26-Sep-05 23:54
Datac0re26-Sep-05 23:54 
QuestionVB? Pin
jonnyO1-Jul-05 0:05
jonnyO1-Jul-05 0:05 
AnswerRe: VB? Pin
Datac0re1-Jul-05 10:32
Datac0re1-Jul-05 10:32 
GeneralRe: VB? Pin
jonnyO1-Jul-05 14:34
jonnyO1-Jul-05 14:34 
GeneralInheriting from RequiredValidator Pin
terry.aney30-Apr-05 10:42
terry.aney30-Apr-05 10:42 
GeneralClientSide updates: an quick and powerful improvement to your control Pin
DanielHac12-Feb-05 6:58
DanielHac12-Feb-05 6:58 
GeneralRe: ClientSide updates: an quick and powerful improvement to your control Pin
Datac0re12-Feb-05 7:26
Datac0re12-Feb-05 7:26 
GeneralRe: ClientSide updates: an quick and powerful improvement to your control Pin
DanielHac12-Feb-05 7:31
DanielHac12-Feb-05 7:31 
GeneralChild control Pin
toxaq9-Jan-05 20:20
toxaq9-Jan-05 20:20 

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

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