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.
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.
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 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.
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.
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.
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:
private bool CheckCompareCondition()
bool isRequired = false;
string compareValue = this.GetControlValidationValue(ControlToCompare);
if (compareValue == TriggerValue)
isRequired = true;
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
controlValue = this.GetControlValidationValue(this.ControlToValidate);
if (controlValue == null)
return controlValue.Trim() != this.InitialValue.Trim();
if (val.controltocompare != "")
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.
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:
evaluationfunction attribute. This method is called by the
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
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 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.
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:
<%@ Register TagPrefix="BNewtz" Namespace="BNewtz.Controls"
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.
- 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.