views:

650

answers:

6

I have a CheckBoxList that I am trying to validate that at least one of the checkboxes is checked.

Markup:

<asp:CustomValidator ID="RequiredFieldValidator8" ValidationGroup="EditArticle"
    runat="server" ErrorMessage="At least one Category is required."
    OnServerValidate="topic_ServerValidate" />
<asp:CheckBoxList id="checkboxlistCategories" runat="server"></asp:CheckBoxList>

Code-behind:

protected void topic_ServerValidate(object source, ServerValidateEventArgs args)
{
    int i = 0;
    foreach (ListItem item in checkboxlistCategories.Items)
    {
        if (item.Selected == true)
            i = i + 1;
    }
    if (i == 0)
        args.IsValid = false;
    else
        args.IsValid = true;
}

If I add ControlToValidate="checkboxlistCategories" in the CustomValidator control, it blows up! The exception I get is:

System.Web.HttpException: Control 'checkboxlistCategories' referenced by the ControlToValidate property of 'RequiredFieldValidator8'

Am I missing something?

A: 

Considering that your server side validation method topic_ServerValidate is tied specificly to the checkboxlistCategories field, you do not need to set the ControlToValidate property.

The reason that it "blows up" when you add the ControlToValidate property, is the type of the control you are referencing - a CheckBoxList. When assigning a ControlToValidate to any kind of validator, that validator will automatically do a "non empty" check on the referenced control before performing the actual validation logic. If the field is empty, no validation will be done (i.e. validation success). That is, of course, unless you set ValidateWhenEmpty = true. Apparently, the validators do not know how to check if a CheckBoxList is empty, and instead they throw an exception.

In your specific case, where the CustomValidator really just serves as non-empty check, you definatly want validator to occur even when the referenced control is left "empty". As stated in the first paragraph of my answer, the simplest way to achieve this is to remove the offending - and unnecessary - property.

Jørn Schou-Rode
when I tried without ControlToValidate="checkboxlistCategories", the OnServerValidate method does not fire.
Neil
@Neil: Are you sure? I have just tested with a trimmed down form containing a `CustomValidator` (with only the `ServerValidate` event handler set), a `CheckBoxList` and a `Button`. Clicking the button sure fires the validation event handler here.
Jørn Schou-Rode
If I add Page.Validate() to the page_load in if (!IsPostBack) and the OnServerValidate event fires every time, but not in (IsPostBack) or in the button click event
Neil
@Neil: I am not sure I follow you completely. Why do you call `Page.Validate()` explicitly? Validation should occur automatically when clicking the button, and you only need to check if `Page.IsValid` in the click event handler.
Jørn Schou-Rode
I call Page.Validate() in my button event, right before Page.IsValid to ensure that the server side event is executed. Any idea?
Neil
Unless you have `CausesValidation = false` on the `Button`, you shouldn't need to call `Page.Validate()` explicitly.
Jørn Schou-Rode
A: 

Just a suggestion, but you could use radio buttons instead and define a group name. This would eliminate the need for the validation b/c only one radio button can be checked within the group.

Eric
The code in the question will set `IsValid` if **one or more** checkboxes have been selected. Radio buttons will accept only one to be selected at any time.
Jørn Schou-Rode
A: 

Maybe I'm missing something. But CheckBoxList control does not have the ValidationPropertyAttribute. Thus you can't use it as ControlToValidate property. Try this:

CheckBoxList.cs

[ValidationProperty("SelectedIndex")]
public class CheckBoxList : System.Web.UI.WebControls.CheckBoxList
{
}

Web.Config

<tagMapping>
  <add tagType="System.Web.UI.WebControls.CheckBoxList, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" mappedTagType="Namespace.CheckBoxList, Assembly"/>
</tagMapping>

Anyway Scott Mitchell has written a great article about it here.

Mehdi Golchin
A: 

To add to this, I am getting a little closer.

If I add Page.Validate() to the page_load in if (!IsPostBack) and the OnServerValidate event fires every time, but not in (IsPostBack) or in the button click event

Neil
before executing your serverside operation code u should check Page.IsValid property also...
dankyy1
+1  A: 

As Mehdi mentioned, 4 Guys covered this in the article he links to in his answer.

For brevity, here's the code listing-

public class CheckBoxListRequiredFieldValidator : BaseValidator
{
    private ListControl _listctrl;

    public CheckBoxListRequiredFieldValidator()
    {
        base.EnableClientScript = false;
    }

    protected override bool ControlPropertiesValid()
    {
        Control ctrl = FindControl(ControlToValidate);

        if (ctrl != null)
        {
            _listctrl = (ListControl)ctrl;
            return (_listctrl != null);
        }
        else
            return false;  
    }

    protected override bool EvaluateIsValid()
    {
        return _listctrl.SelectedIndex != -1;
    }
}

Pop this badger into you user controls library and away you go.

HTH.

Stimul8d
A: 

Here's a cleaner jQuery implementation that allows one ClientValidationFunction for any number of CheckBoxList controls on a page:

function ValidateCheckBoxList(sender, args) {
 args.IsValid = false;

 $("#" + sender.id).parent().find("table[id$="+sender.ControlId+"]").find(":checkbox").each(function () {
     if ($(this).attr("checked")) {
  args.IsValid = true;
  return;
     }
 });
}

Here's the markup:

<asp:CheckBoxList runat="server"
    Id="cblOptions" 
    DataTextField="Text" 
    DataValueField="Id" />

<xx:CustomValidator Display="Dynamic" 
        runat="server" 
        ID="cblOptionsValidator"
        ControlId="cblOptions"
        ClientValidationFunction="ValidateCheckBoxList" 
        ErrorMessage="One selection required." />

And finally, the custom validator that allows the client function to retrieve the target control by ID:

public class CustomValidator : System.Web.UI.WebControls.CustomValidator
{
 public string ControlId { get; set; }

 protected override void OnLoad(EventArgs e)
 {
     if (Enabled)
         Page.ClientScript.RegisterExpandoAttribute(ClientID, "ControlId", ControlId);

     base.OnLoad(e);
 }
}
Nariman