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.

7 comments:

Juan Manuel said...

Thanks. I was about to do it myself when I found this entry

Evan Freeman said...

Your welcome.

Markus said...

same here. thanks!

Praveen said...
This comment has been removed by a blog administrator.
Anonymous said...

Hi Evan, thanks for share.
how can i implement this?

Evan Freeman said...

Well there are a few ways. Best to just wrap it into a dll and add it to you're toolbox.

Probably google a bit about the subject.

Kshitij said...

First of all, Thanks a lot! You have indeed saved me a lot of time. I have implemented your code. I have used JavaScript to access the value attribute. The problem is that I am unable to get the modified value on the server side. That is, on the server, I still get the old value when I do a Items.Value. Any help on this problem would be appreciated.