I have been using CheckBoxes and CustomValidators for quite some time now, but never did I know that I will be presented with an issue that I never encountered before and that issue is regarding dynamic CheckBoxes and dynamic CustomValidators. To give you an idea of what I am talking about, if you had developed a website that has that “Agree to Terms and Conditions” required before continuing, then your guess is a bit near. The only difference is making the items dynamic where these questions are pulled from the database, that database table also defines whether the tick is required or not.
For the first scenario (non dynamic), it's easy as you only need these controls:
<asp:CheckBox ID="CheckBox1" runat="server" />
<asp:CustomValidator ID="CustomValidator1" runat="server" ErrorMessage="CustomValidator"
ClientValidationFunction="ValidateCheckbox"></asp:CustomValidator>
and this JavaScript to be consumed by the CustomValidator:
<script language="JavaScript" type="text/javascript">
function ValidateCheckbox(source, args) {
args.IsValid = args.IsValid = document.getElementById
('<%= CheckBox1.ClientID %>').checked;
}
</script>
But what if you have 50 checkboxes, will you create one script for each one of them? Wouldn’t it be nice to just pass a parameter to your JavaScipt? By default, you cannot do that as the JavaScript that the CustomValidator uses only 2 parameters which is the source and args. But don’t worry, we can use RegisterExpandoAttribute
method to extend an object's attribute.
So what does that method do? The answer is simple. It registers a name/value pair as a custom attribute of the specified control. Having that in mind, we use it in the CustomValidator and extend its attributes to have the ClientID of the checkbox you want to validate. So for clarity, refer to the example below.
So let's say we have the following controls that are dynamically driven by the GridView
:
<asp:GridView ID="myGridView" runat="server" AutoGenerateColumns="False"
OnRowDataBound="myGridView_RowDataBound">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="lblQuestionItem" runat="server"
Text='<%# Bind("QuestionItem") %>'
CssClass="Normal"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:HiddenField ID="hdnIsRequired" runat="server"
Value='<%# Bind("IsRequired") %>' />
<asp:CheckBox ID="chkAnswer" runat="server"
CssClass='<%# Bind("ControlClass") %>' />
<asp:CustomValidator ID="ctmCHKAnswer"
ClientValidationFunction="ValidateCurrentCheckbox"
runat="server" ErrorMessage="*"></asp:CustomValidator>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Now from the sample above, we defined a CustomValidator and that’s the object that we will extend the attributes to contain the chkAnswer‘s client ID, to do that here is the code behind, also remember that you need to do that on a per row basis as well. That's why it is in myGridView_RowDataBound
, and here is how to achieve it.
protected void myGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
HiddenField hdnIsRequired = (HiddenField)e.Row.FindControl("hdnIsRequired");
CheckBox chkAnswer = (CheckBox)e.Row.FindControl("chkAnswer");
CustomValidator ctmCHKAnswer = (CustomValidator)e.Row.FindControl("ctmCHKAnswer");
Page.ClientScript.RegisterExpandoAttribute
(ctmCHKAnswer.ClientID, "ClientID", chkAnswer.ClientID, false);
ctmCHKAnswer.Enabled = bool.Parse(hdnIsRequired.Value);
}
}
If you noticed, we extended ctmCHKAnswer
with the “ClientID” attribute that comes from chkAnswer.ClientID
. So when you fire the JavaScript, you can then use the source parameter and use source.ClientID
to get the checkbox
client ID you need.
<script language="JavaScript" type="text/javascript">
function ValidateCurrentCheckbox(source, args) {
var checkbox = document.getElementById(source.ClientID)
args.IsValid = checkbox.checked;
}
</script>