Thursday, November 20, 2008

Client Side Values for the CheckBoxList

I have run into the problem so many times now that I have finally, after years of waiting for Microsoft to fix it, broke down and fixed it myself.

The problem that many people run into with the CheckBoxList in todays world of Ajax is that it does not render the value attribute on the client side. So you can't access it through javascript. This is actually intentional as the attributes are removed prior to rendering, well probably more of an oversite than intentional.

Of course you have the values on the server side, but that is because they are stored in control state and rebound when the control is recreated on postback.

The following code extends the CheckBoxList and fixes the RenderItem method to render a value attribute for the checkboxes in the list. I kept the changes minimal just so I didn't have to fix data binding and viewstate, so the control works exactly the same as it always has except it renders values on the client. I hope this helps someone out.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Globalization;
using System.Security.Permissions;


namespace System.Web.UI.WebControls{
[AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.LinkDemand,
Level = AspNetHostingPermissionLevel.Minimal),
ToolboxData("<{0}:WorkingCheckBoxList runat=\"server\" />")]
public class CheckBoxListWithClientValues : CheckBoxList
{
private CheckBox _controlToRepeat;
private bool _cachedIsEnabled;
private bool _cachedRegisterEnabled;

public CheckBoxListWithClientValues()
{
this._controlToRepeat = new CheckBox();
this._controlToRepeat.EnableViewState = false;
this._controlToRepeat.ID = "0";
this.Controls.Add(this._controlToRepeat);
}

protected override void RenderItem(ListItemType itemType,
int repeatIndex,
RepeatInfo repeatInfo,
System.Web.UI.HtmlTextWriter writer)
{
if (repeatIndex == 0)
{
this._cachedIsEnabled = base.IsEnabled;
this._cachedRegisterEnabled = ((this.Page != null) && base.IsEnabled);
}

ListItem item = this.Items[repeatIndex];
this._controlToRepeat.Attributes.Clear();

if (item.Attributes.Count > 0) //has attributes
{
foreach (string str in item.Attributes.Keys)
{
this._controlToRepeat.Attributes[str] = item.Attributes[str];
}
}
this._controlToRepeat.ID = repeatIndex.ToString(NumberFormatInfo.InvariantInfo);
this._controlToRepeat.Text = item.Text;
this._controlToRepeat.Checked = item.Selected;
this._controlToRepeat.EnableViewState = true;
this._controlToRepeat.Enabled = this._cachedIsEnabled && item.Enabled;
this._controlToRepeat.InputAttributes.Add("value", item.Value); //add the value attribute to be rendered
this._controlToRepeat.RenderControl(writer);
}
}
}


A side not here is that it still renders an HTML table just the same as it always has. Many argue that that is a bad idea, and an unordered list is better, I agree with the later, unordered lists are smaller, more managable, and how a list of anything should be rendered.