When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article
Jobs

ASP ASP.NET ASP FAQs Message Board Feedback ASP Jobs
 
Print this Page!
Published: Wednesday, September 20, 2006

Creating Validator Controls for the CheckBox and CheckBoxList

By Scott Mitchell


Introduction


ASP.NET provides a variety of validation Web controls that can be used to validate a user's form field inputs. (See Form Validation with ASP.NET - It Doesn't Get Any Easier! for general information on the validation Web controls and Dissecting Validation Controls in ASP.NET 2.0 for the validation control changes from ASP.NET version 1.x to ASP.NET version 2.0.) Unfortunately, the validation Web controls do not work with the CheckBox or CheckBoxList Web controls. If you set a validation control's ControlToValidate property to the ID of a CheckBox or CheckBoxList, the page will throw an HttpException, stating: "Control 'controlID' referenced by the ControlToValidate property of 'validationControlID' cannot be validated."

There may be times, however, when you need to provide validation for a CheckBox or CheckBoxList. Many Web pages with Terms of Service include a CheckBox titled "I agree to the above terms" that must be checked before continuing. Likewise, a Web Form may contain a set of options in the form of a CheckBoxList. Perhaps the user is required to check at least one of these options before continuing. To provide such validation, we have three choices:

  1. Forgo any sort of validation Web control semantics and perform the validation check using code on postback. The downside of this is that it breaks from the standard validation control metaphor and requires extra effort to include client-side validation.
  2. Use the CustomValidator control and define our own server-side and client-side validation logic. The benefit of this approach is that it adheres to the validation control metaphor; however, the validation logic is tightly bound to the ASP.NET page, meaning that the server-side and client-side validation must be replicated on all pages that need to validate a CheckBox or CheckBoxList. (See Using the CustomValidator Control for more information on this topic.)
  3. Create a custom, compiled validation server control that provides the functionality needed. The benefit of this approach is that we have a reusable, easily deployable custom server control that adheres to the validation control metaphor. Unfortunately, this option requires the most upfront code/effort.
In this article we'll implement the third option, creating two custom server controls, CheckBoxValidator and CheckBoxListValidator. The download at the end of this article includes both the entire source code and a compiled assembly that you can drop into your ASP.NET 2.0 web applications. Read on to learn more!

The server controls provided in this article were designed using Visual Studio 2005 and the .NET Framework 2.0's base class libraries; consequently, they will only work in ASP.NET 2.0 applications. If you are still using ASP.NET 1.x, check out Creating a Validation Control for CheckBoxLists. Alternatively, you may adapt the code presented here to work in ASP.NET 1.x.

- continued -

Creating CheckBox and CheckBoxList Validators in ASP.NET 1.x
The server controls examined in this article were designed using Visual Studio 2005 and the .NET Framework 2.0's base class libraries; consequently, they will only work in ASP.NET 2.0 applications. If you are still using ASP.NET 1.x, check out Creating a Validation Control for CheckBoxLists. Alternatively, you may adapt the code presented here to work in ASP.NET 1.x.

Understanding the ASP.NET Validation Workflow


A validation control's primary responsibility is to determine whether or not its assigned control's value is valid. What constitutes validity depends on the validation control - a RequiredFieldValidator, for example, checks to ensure that its control's current value doesn't equal the specified InitialValue. Secondarily, validation controls can be grouped into validation groups, be validated through client script, and so on. Many of these requirements are provided by properties and methods in the BaseValidator class (found in the System.Web.UI.WebControls namespace). Not surprisingly, all of the built-in ASP.NET validation controls extends the BaseValidator class, adding their own customizations.

In a short while we'll create our own custom validation Web controls for validating a CheckBox and CheckBoxList. These custom classes will inherit the BaseValidator class, overriding the following methods:

  • EvaluateIsValid() - this is the only BaseValidator class that must be overridden. It returns a Boolean value that indicates the validation control's validity.
  • ControlPropertiesValid() - this method returns a Boolean indicating if the control being validated is one that can be. In the BaseValidator class, it checks to ensure that the ControlToValidate property is set and that the control referenced has a property that can be validated. We need to override this method because this method is the one that throws an exception if ControlToValidate is set to the ID of a CheckBox or CheckBoxList.
  • OnPreRender(EventArgs) and AddAttributesToRender(HtmlTextWriter) - these methods inject client-side script to perform client-side validation (if the user is visiting with an uplevel browser and the validation control's EnableClientScript property is True (the default).
In addition to these overridden methods, we'll also add a property to both the CheckBoxValidator and CheckBoxListValidator classes. In the CheckBoxValidator class, we'll add a MustBeChecked property that indicates whether the CheckBox needs to be checked or unchecked to be considered valid. In the CheckBoxListValidator class, the MinimumNumberOfSelectedCheckBoxes property indicates the least number of CheckBoxList items that must be selected for validity. Both controls provide client-side validation support.

The remainder of this article examines the CheckBoxValidator control in detail (the CheckBoxListValidator control is not discussed in this article, but is included in the code available at the end of this download).

The Core CheckBoxValidator Custom Validation Control Members


In both the CheckBoxValidator and CheckBoxListValidator classes I added a protected helper property to return a reference to the control being validated. Typically, the BaseValidator class's GetControlValidationValue() method can be referenced to get back the value being validated, but with a CheckBox or CheckBoxList, a call to this method will throw an HttpException. Therefore, the control value is accessed through the helper property. The following code shows the CheckBoxValidator class's helper property and ControlPropertiesValid() and EvaluateIsValid() methods:

namespace skmValidators
{
    public class CheckBoxValidator : BaseValidator
    {
       private CheckBox _ctrlToValidate = null;
       protected CheckBox CheckBoxToValidate
       {
            get
            {
                if (_ctrlToValidate == null)
                   _ctrlToValidate = FindControl(this.ControlToValidate) as CheckBox;
               
                return _ctrlToValidate;
            }
       }

       protected override bool ControlPropertiesValid()
       {
            // Make sure ControlToValidate is set
            if (this.ControlToValidate.Length == 0)
                throw new HttpException(string.Format("The ControlToValidate property of '{0}' cannot be blank.", this.ID));

            // Ensure that the control being validated is a CheckBox
            if (CheckBoxToValidate == null)
                throw new HttpException(string.Format("The CheckBoxValidator can only validate controls of type CheckBox."));

            return true;    // if we reach here, everything checks out
       }

       protected override bool EvaluateIsValid()
       {
            // Make sure that the CheckBox is set as directed by MustBeChecked
            return CheckBoxToValidate.Checked == MustBeChecked;
       }
      
       ... some methods/properties omitted for brevity ...
    }
}

First, note that the CheckBoxValidator class extends the BaseValidator class. Next, examine the CheckBoxToValidate property. It uses the FindControl("controlID") method to find the control with the specified ControlToValidate value. If no control with that ID is found, or if it's not a CheckBox, the property returns null.

The ControlPropertiesValid() method, which is responsible for insuring that the essential validation control properties are set and are valid, starts by checking the ControlToValidate property. If it's not specified, an HttpException is thrown. Likewise, if it cannot find a control with the specified ControlToValidate or if that control is not a CheckBox, an HttpException is thrown. If those two checks pass, then the method returns True, indicating that the control properties are valid.

The EvaluateIsValid() method is invoked whenever the validation control's validity needs to be determined. This method returns True - indicating validity - only if the CheckBox to validate's Checked property is set as dictated by the MustBeChecked property. That is, if MustBeChecked is True, the CheckBoxValidator control returns True only if its CheckBox is checked; conversely, if MustBeChecked is False, the CheckBox must be unchecked for the validation control to indicate validity.

Adding Client-Side Validation Support


At this point, we have all of the needed server-side functionality for the CheckBoxValidator control. To add client-side functionality, however, we must add a bit more code. The BaseValidator class automatically adds client-side script for handling the plumbing of client-side validation: performing validation when the form submits, determining what client-side function to invoke for validating each validator, and so on. All we need to do is inject the client-side function that performs the validation, specify the client-side validation function to invoke, and add any properties needed for client-side validation to the page's rendered markup (for the CheckBoxValidator control, we need to persist the value of the MustBeChecked property to the page's rendered markup to indicate whether the CheckBox needs to be checked or unchecked to be valid).

These two tasks are handled in two separate methods. The client-side script used for validation should be injected in the OnPreRender(EventArgs) method, whereas the name of the validation function and any additional propeties are added in the AddAttributesToRender(HtmlTextWriter) method.

Let's start with the OnPreRender(EventArgs) method. The following code starts by calling the base class's (BaseValidator's) OnPreRender(EventArgs) method, which injects the common client-side script. Next, the RegisterClientScriptInclude() method is used to add a script include (<script src="urlToScript" ... />) to the JavaScript file embedded within the CheckBoxValidator class's assembly. (Embedding resources is an easy and efficient means of deploying and referencing external resource files with a server control library; see Accessing Embedded Resources through a URL using WebResource.axd for more information.) EvaluateIsValid() methods:

namespace skmValidators
{
    public class CheckBoxValidator : BaseValidator
    {
       ... Some methods/properties omitted for brevity ...

       protected override void OnPreRender(EventArgs e)
       {
            base.OnPreRender(e);

            // Register the client-side function using WebResource.axd (if needed)
            // see: http://aspnet.4guysfromrolla.com/articles/080906-1.aspx
            if (this.RenderUplevel && this.Page != null && !this.Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), "skmValidators"))
                this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "skmValidators", this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "skmValidators.skmValidators.js"));
       }
    }
}

The client-side validation function must accept a single input parameter (a reference to the validation control) and must return a Boolean value indicating the validity of the validation control. This function for the CheckBoxValidator control is blindingly simple:

function CheckBoxValidatorEvaluateIsValid(val)
{
    var control = document.getElementById(val.controltovalidate);
    var mustBeChecked = Boolean(val.mustBeChecked == 'true');

    return control.checked == mustBeChecked;
}

The ID of the control being validated is available via the controltovalidate property of the passed-in object; a call to the document.getElementById("id") function returns a reference to the actual control. The mustBeChecked property of the validation control is then accessed. Finally, the function returns True only if the control's checked property matches up to the mustBeChecked property value.

At this point there are two questions facing us. First, how does the common script injected by the BaseValidator know to call the CheckBoxValidatorEvaluateIsValid function to validate the CheckBoxValidator control? And second, how is the MustBeChecked property value specified for the CheckBoxValidator control passed to this client-side function? If you do a View/Source of a page with validation controls on it, you'll find a JavaScript section that spells out this (and other) information. For example, a RequiredFieldValidator has a select set of its properties rendered in client-side script like so:

var RequiredFieldValidator1 = document.all ? document.all["RequiredFieldValidator1"] : document.getElementById("controlID");
RequiredFieldValidator1.controltovalidate = "controlToValidateID";
RequiredFieldValidator1.errormessage = "errorMessage";
RequiredFieldValidator1.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";

There may be more settings there, depending on what properties you've set for the RequiredFieldValidator, but the concept is the same - property values needed by the client-side validation logic must be persisted to the client-side through the page's rendered markup. This includes, at minimum, the ID of the control to validate and the function responsible for validation.

This information is injected into the page's rendered markup via the validation control's AddAttributesToRender(HtmlTextWriter) method, and can be injected in two ways:

  • Using the AddAttribute method of the passed-in HtmlTextWriter object. This adds attributes to the validation Web control's rendered HTML element.
  • Using the RegisterExpandoAttribute method, which adds the attributes using client-side script, like in the RequiredFieldValidator script block shown above.
The BaseValidator class adds some of the control's needed state automatically (such as the controltovalidate) and chooses which method to use based on the page's XHTML conformance settings. The logic used to determine these settings are, unfortunately, internal, meaning that they cannot be called from an assebly outside of the .NET Framework. I've attempted to do my best to replicate the logic in a Helpers class. The reason this matters is because, in my testing, I found that for some browsers it was essential that the properties all be defined the same way. Internet Explorer was happy to have some properties defined using the expando approach, and others as inline attributes, but FireFox wouldn't recognize the inline attribute values when other values were specified using the expando technique.

Enough minutia, let's look at the code! As you can see below, two attributes are added (either via the expando or AddAttribute approaches, depending on the Helpers.EnableLegacyRendering() method's return value). The first attribute is required and specifies the name of the function to invoke to perform client-side validation. The second attribute persists the MustBeChecked property to the rendered output.

namespace skmValidators
{
    public class CheckBoxValidator : BaseValidator
    {
       ... Some methods/properties omitted for brevity ...

       protected override void AddAttributesToRender(HtmlTextWriter writer)
       {
            base.AddAttributesToRender(writer);

            // Add the client-side code (if needed)
            if (this.RenderUplevel)
            {
                // Indicate the mustBeChecked value and the client-side function to used for evaluation
                // Use AddAttribute if Helpers.EnableLegacyRendering is true; otherwise, use expando attributes
                if (Helpers.EnableLegacyRendering())
                {
                   writer.AddAttribute("evaluationfunction", "CheckBoxValidatorEvaluateIsValid", false);
                   writer.AddAttribute("mustBeChecked", MustBeChecked ? "true" : "false", false);
                }
                else
                {
                   this.Page.ClientScript.RegisterExpandoAttribute(this.ClientID, "evaluationfunction", "CheckBoxValidatorEvaluateIsValid", false);
                   this.Page.ClientScript.RegisterExpandoAttribute(this.ClientID, "mustBeChecked", MustBeChecked ? "true" : "false", false);
                }
            }
       }
    }
}

Using the CheckBoxValidator and CheckBoxListValidator Controls


To get started using the CheckBoxValidator and CheckBoxListValidator controls in your ASP.NET 2.0 web application, download the code at the end of this article and drop the skmValidators assembly into your application's /Bin folder. You can add the validation controls to Visual Studio 2005's Toolbox - right click on the Toolbox, select Choose Items and browse to skmValidators.dll - or you can manually add the controls using declarative markup and the <%@ Register %gt; directive.

Once you've added the controls to the Toolbox or have entered their markup manually, they should appear as any validation control when in the Designer. Moreover, their properties should be accessible through the Properties window. And most importantly, when visiting a page through a browser, they should function as expected!

The CheckBoxValidator in the Designer in Visual Studio


The CheckBoxValidator in the Designer in Visual Studio


The CheckBoxValidator When Viewed Through a Web Page


The CheckBoxValidator When Viewed Through a Web Page

At the end of this article you'll find links to related articles, the complete project code that includes a test project with a demo page. In this article we only examined the CheckBoxValidator control; the download includes an additional validation control designed to work with CheckBoxLists.

The CheckBoxValidator Control Has Been Upgraded!
I have upgraded the CheckBoxValidator so that it can optionally enable or disable an associated Button, LinkButton, or ImageButton control depending on its CheckBox's checked state.

The submit button is disabled when the CheckBox is unchecked.

For more information, see Disabling the Submit Button Until a CheckBox is Checked.

Happy Programming!

  • By Scott Mitchell


    Further Readings:


  • Validating User Input in ASP.NET Web Pages
  • Form Validation with ASP.NET - It Doesn't Get Any Easier!
  • Dissecting Validation Controls in ASP.NET 2.0
  • Accessing Embedded Resources through a URL using WebResource.axd
  • Disabling the Submit Button Until a CheckBox is Checked
  • Attachments


  • Download the skmValidator Controls and a Demo Website Application (in ZIP format)


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article