How to Validate Controls Only if Certain Conditions are Met.

Introduction

If you are like me you have been in a situation many times before where you needed to validate the input of controls on your web form only if certain conditions existed. For example, an order form commonly presents two sets of fields for collecting the customer's billing and shipping addresses. In many cases the two addresses are identical so a checkbox is present to indicate as such and therefore only one set needs to be completed. To implement this with the existing framework validation controls you'd have to use several instances of the CustomValidator control. There's certainly nothing wrong with this but I wasn't satisfied with that. I wanted to extend the framework to make this situation much easier. Wouldn't it be great if you could write one method in your code-behind and one javascript method where you would write the logic for determining whether validation should be performed or not and have all your validation controls use this same logic? In fact you can and I will show you how.

Extending the BaseValidator class

Microsoft describes the BaseValidator class as "Serves as the abstract base class for validation controls." Every control we create must inherit this class or a descendant of this class. Normally when you inherit directly from it you're required to override the EvaluateIsValid method which is where all the logic is executed to validate a control. The first class we'll create in this tutorial will be called BaseConditionalValidator and inherits directly from BaseValidator. However, we won't be implementing EvaluateIsValid at this level. We're going to make it abstract/MustInherit simply to add some extra functionality for all conditional validation controls. It is here where we will add all the logic so we can accomplish the goal of allowing multiple validators use the same logic to test whether validation should be performed.

At the server level the simplest way to implement this logic is to publish an event which will be called at the beginning of validation. It can pass a class which will expose a property allowing the subscribing page to indicate whether to continue with validation or not. We will name this event Validating and it will be of type System.ComponentModel.CancelEventHandler. The reason I chose this type of event is because this type will pass an instance of CancelEventArgs which has a property called "Cancel" thereby meeting our needs. We will also create a protected method called OnValidating to raise it.

protected virtual void OnValidating(CancelEventArgs e)

{

if (Validating != null)

{

Validating(this, e);

}

}

 

public event CancelEventHandler Validating;

At the client level we'll create a javascript file that will contain all our code so very little needs to be done in the BaseConditionalValidator class. However, to create similar behavior on the client we'll create a property called ClientEvaluateIsRequiredFunction of type String. The purpose of this property is to store the name of the javascript function to call at the beginning of a control's validation. More on this later. For now here is the syntax for the property.

public virtual string ClientEvaluateIsRequiredFunction

{

get

{

object v = ViewState["ClientEvaluateIsRequiredFunction"];

return (v != null) ? v.ToString() : string.Empty;

}

set

{

ViewState["ClientEvaluateIsRequiredFunction"] = value;

}

}

I mentioned that we'll add all our javascript to an external file. If you place all this code into an assembly then you'll want to embed the js file into the assembly. A couple steps necessary here. First, set the Build Action of the file to "Embedded Resource." Then add an assembly attribute above the declaration of BaseConditionalValidator as such.

[assembly: System.Web.UI.WebResource("AspNetLibraryCS.ConditionalValidation.js", "text/javascript")]

The last thing to do in the BaseConditionalValidator class is to override the OnPreRender method. What we're trying to accomplish in this method is to add the information that is needed on the client. As I'm sure you've noticed in the rendered markup of one of your forms with validation there are several lines of javascript at the bottom. In a nutshell there are objects being created there and their properties being set. All that is used for validation and there is one property we need to add to this. It is the value of the ClientEvaluateIsRequiredFunction property. Later in this lesson you'll see how this value is used.

protected override void OnPreRender(EventArgs e)

{

/* If client script is enabled add the necessary information to perform

* validation. */

 

if (EnableClientScript)

{

Page.ClientScript.RegisterExpandoAttribute(ClientID, "clientevaluateisrequiredfunction", ClientEvaluateIsRequiredFunction);

 

string resourceUrl = Page.ClientScript.GetWebResourceUrl(typeof(BaseConditionalValidator), "AspNetLibraryCS.ConditionalValidation.js");

 

Page.ClientScript.RegisterClientScriptInclude("ConditionalValidation", resourceUrl);

}

 

base.OnPreRender(e);

}

Creating a conditional RequiredFieldValidator control

Now that are base class is setup we'll go through the steps of creating a control that will work exactly like the RequiredFieldValidator control with the added functionality of conditionally preventing validation. The RequiredFieldValidator control adds one property to the interface which is InitialValue. As you already know this allows you to validate against values other than just an empty string. I use this with the DropDownList control a lot when I don't want to set the first list item's value to empty. It's also useful when you want to add a message to a textbox like "Enter Your Name." Here is our implementation of this property.

public virtual string InitialValue

{

get

{

object v = ViewState["InitialValue"];

return (v != null) ? v.ToString() : string.Empty;

}

set

{

ViewState["InitialValue"] = value;

}

}

Just as in the BaseConditionalValidator's OnPreRender method we need to add some property values for this class. Only two and those are the InitialValue and the function to call for validating. The function to call is required for all validation controls. The name given to the attribute is "evaluationfunction." In our case the value will be "ConditionalRequiredFieldEvaluateIsValid."

protected override void OnPreRender(EventArgs e)

{

/* If client script is enabled add the necessary information to perform

* validation. */

 

if (EnableClientScript)

{

Page.ClientScript.RegisterExpandoAttribute(ClientID, "initialvalue", InitialValue);

Page.ClientScript.RegisterExpandoAttribute(ClientID, "evaluationfunction", "ConditionalRequiredFieldEvaluateIsValid");

}

 

base.OnPreRender(e);

}

Now we're getting to the good stuff. Overriding the EvaluateIsValid method. This method takes no parameters and returns a Boolean with a value of true indicating that the control is valid and false indicating it's not valid. The first step of the method will be to check whether validation should be performed. Three steps are required here.

  1. Create an instance of CancelEventArgs
  2. Raise the Validating event
  3. Check the value of the Cancel property and if true then return true bypassing the rest of the validation logic.

CancelEventArgs args = new CancelEventArgs();

OnValidating(args);

if (args.Cancel) return true;

There are two situations where the Cancel property would be false after the last block of code. Either the consuming page determined that validation should be performed or the Validating event wasn't handled in which case this control will operate exactly like the RequiredFieldValidator. In any case, once this point is reached we will proceed with validating the control.

string valueToValidate = GetControlValidationValue(ControlToValidate).Trim();

string initial = InitialValue.Trim();

 

if (!valueToValidate.Equals(initial))

{

return true;

}

else

{

return false;

}

Setting up the javascript

Earlier in this tutorial I mentioned that we would create an external javascript file. This file will contain all the logic that will be needed on the client. First thing we'll setup are two methods which will create a similar behavior to our Validating event for the server code. The OnValidating method will accept two parameters. The first is the actual validation control. The second is an instance of CancelEventArgs. As you can see in the following code CancelEventArgs is a simple class declaration with the Cancel property added to it. OnValidating will check to see if a value is present for the ClientEvaluateIsRequiredFunction. If it is then it will call that method passing the instance of CancelEventArgs. Very simple to implement and closely resembles our server code.

function OnValidating(val, e)

{

if (val.clientevaluateisrequiredfunction.length > 0)

{

eval(val.clientevaluateisrequiredfunction + '(val, e);');

}

}

 

function CancelEventArgs()

{

this.Cancel = false;

}

Finally the method to be called for validation. The ConditionalRequiredFieldEvaluateIsValid function. As required by the framework it accepts just one parameter which is the validation control. Our server code first check whether validation should be performed. That is exactly what we will do here. Three lines of code that are almost identical to the server code.

function ConditionalRequiredFieldEvaluateIsValid(val)

{

var args = new CancelEventArgs();

OnValidating(val, args);

if (args.Cancel) return true;

 

var valueToValidate = document.getElementById(val.controltovalidate).value.trim();

var initialValue = val.initialvalue.trim();

 

if (valueToValidate != initialValue)

{

return true;

}

else

{

return false;

}

}

How to use these classes

Now that we've built the controls let's go through an example of implementing them. Below is the markup of a sample aspx page with a textbox, an instance of ConditionalRequiredFieldValidator, a checkbox and a button. The checkbox will be used by the code to turn validation on or off.

<asp:TextBox ID="FirstName" runat="server" /><uc:ConditionalRequiredFieldValidator ID="FirstNameRequired" runat="server" ClientEvaluateIsRequiredFunction="IsRequired" ControlToValidate="FirstName" EnableClientScript="false" ErrorMessage="Required" OnValidating="Main_Validating" /><br />

 

<asp:CheckBox ID="chkValidate" runat="server" Text="Validate" /><br />

 

<asp:Button ID="btnSubmit" runat="server" Text="Submit" />

Our Validating event handler will check the Checked property of the checkbox. If it is checked than nothing further needs to be done. If it isn't checked the Cancel property will be set to true. Test this out by setting the validation's EnableClientScript to false so that a full postback will occur before validation.

protected void Main_Validating(object sender, System.ComponentModel.CancelEventArgs e)

{

if (ValidateValue.Checked)

{

e.Cancel = false;

}

else

{

e.Cancel = true;

}

}

Lastly, our javascript implementation for the Validating event. It will take two arguments just as the code-behind does. It will then check the value of the checkbox. If checked than nothing further to do. Otherwise set the Cancel property to true. Play around this functionality both on the client and server. Try adding several validation controls that use the same methods.

function IsRequired(sender, e)

{

if (!document.getElementById('<%= ValidateValue.ClientID %>').checked)

{

e.Cancel = true;

}

}

Summary

Hopefully you'll see some uses for these classes. I've put them to good use in many of my applications. May you do the same!

New Comment

  
  
  
  
Download Source Code
Email Print