If you add a normal RequiredFieldValidator and you want to validate a CheckBoxList, you will get a runtime exception informing you that the CheckBoxList cannot be validated.

Sometimes you want to make sure the that user has selected at least one of the checkbox in the checkbox list, but as aforementioned, if you user a normal RequiredFieldValidator you will get an exception.

You could use a CustomValidator control, but a nicer and more reusable approach is to build a custom web control that extends the BaseValidator.

I found a 2001 article on how to do that (Building a CheckBoxList Validator Control) but it doesn't work properly on ASP.NET 2.0 and also it doesn't work on Firefox.

So I have to make some small changes to make it work on ASP.NET 2.0 and on Firefox.

First thing you have create a class that inherits from BaseValidator

public class RequiredFieldValidatorForCheckBoxLists : BaseValidator

Then you have to override a few methods:

ControlPropertiesValid

This method must check whether the ControlToValidate property of the validator refers to an existing control and if the control is of the correct type

protected override bool ControlPropertiesValid()
{
   Control ctrl = FindControl(ControlToValidate);
   if (ctrl != null)
   {
      CheckBoxList _listctrl = ctrl as CheckBoxList;
      return (_listctrl != null);
   }
   else
      return false;  
}

In this we check if the control is null and if the control can be cast to a CheckBoxList.

EvaluateIsValid

This is the methods that performs the server side validation in case JavaScript is not enabled on the client, or not supported.

protected override bool EvaluateIsValid()
{
   return EvaluateIsChecked();
}

// Private method to perform the real check
private bool EvaluateIsChecked()
{
   CheckBoxList _cbl = ((CheckBoxList)FindControl(ControlToValidate));
   foreach (ListItem li in _cbl.Items)
   {
      if (li.Selected)
      {
         return true;
      }
   }
   return false;
}

In this method I just loop through all the items of the CheckBoxList and if I find one that is selected then I return true.

Now we have the validation working on the server after a postback, but it would be nice if we could perform the validation directly on the client when the user toggles the state of the checkboxes. To accomplish this there is another step to take: inject some client side JavaScript in the OnPreRender method.

OnPreRender

The PreRender event is raised just before the control is rendered on the page, so it the best moment to inject some client side functionality into the page.

protected override void OnPreRender(EventArgs e)
{
   base.OnPreRender(e);
   if (EnableClientScript) { ClientScript(); }
}

private void ClientScript()
{
   StringBuilder sb_Script = new StringBuilder();
   sb_Script.Append("<script language=\"javascript\">");
   sb_Script.Append("\r");
   sb_Script.Append("\r");
   sb_Script.Append("function cb_verify(sender) {");
   sb_Script.Append("\r");
   sb_Script.Append("var val = document.getElementById(document.getElementById"
			+"(sender.id).controltovalidate);");
   sb_Script.Append("\r");
   sb_Script.Append("var col = val.getElementsByTagName(\"*\");");
   sb_Script.Append("\r");
   sb_Script.Append("if ( col != null ) {");
   sb_Script.Append("\r");
   sb_Script.Append("for ( i = 0; i < col.length; i++ ) {");
   sb_Script.Append("\r");
   sb_Script.Append("if (col.item(i).tagName == \"INPUT\") {");
   sb_Script.Append("\r");
   sb_Script.Append("if ( col.item(i).checked ) {");
   sb_Script.Append("\r");
   sb_Script.Append("\r");
   sb_Script.Append("return true;");
   sb_Script.Append("\r");
   sb_Script.Append("}");
   sb_Script.Append("\r");
   sb_Script.Append("}");
   sb_Script.Append("\r");
   sb_Script.Append("}");
   sb_Script.Append("\r");
   sb_Script.Append("\r");
   sb_Script.Append("\r");
   sb_Script.Append("return false;");
   sb_Script.Append("\r");
   sb_Script.Append("}");
   sb_Script.Append("\r");
   sb_Script.Append("}");
   sb_Script.Append("\r");
   sb_Script.Append("</script>");

   //Inject the script into the page
   Page.ClientScript.RegisterClientScriptBlock(GetType(), SCRIPTBLOCK,
					sb_Script.ToString());
   //Registering validator clientside javascript function
   Page.ClientScript.RegisterExpandoAttribute(ClientID, "evaluationfunction",
					"cb_verify");
}

First I check if the client validation is enabled, and, if it is, I write on the page the javascript method, and more important, the code to tell the common validation script to call my "cb_verify" function to validate the value of the control.

If you do a "View Source" on an ASP.NET page you will see something like this:

var validator = document.all ? document.all["validator"] :
		document.getElementById("validator");
validator.controltovalidate = "answer";
validator.isvalid = "False";
validator.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";

These lines add custom properties to the validators registered on the page, so we have to do the same also to specify the function name used to validate the CheckBoxList, using the RegisterExpandoAttribute method:

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

which will add the following code to the page:

checkboxListValidator.evaluationfunction = "cb_verify";

Add the RequiredFieldValidatorForCheckBoxLists to the page

Register the control and then add it to the page

<%@ Register TagPrefix="CC1" Namespace="Custom.Validators"
    Assembly="Custom.Validators" %>

<CC1:RequiredFieldValidatorForCheckBoxLists
    ControlToValidate="answers"
    runat="server"
    ID="checkboxListvalidator">*
</CC1:RequiredFieldValidatorForCheckBoxLists>

Download the complete file

Here is the complete RequiredFieldValidatorForCheckBoxLists.cs file, also without all the extra new lines I added above to make long lines fit into the width of the main column.

Technorati tags: , ,