I am trying to create a custom control that extends the RadComboBox from Telerik to create a Dropdown Checkbox List with default templates. The plan is to use the control in several places so I wanted to consolidate all of the logic in one spot.
However I am experiencing a couple of weird issues on postback. If you check a couple of items and then hit the Apply button the correct items are selected, but the text on the checkbox is different. Then on the next postback I get the error Multiple controls with the same ID 'i2' were found. FindControl requires that controls have unique IDs.
Attached is the custom control. Any help is appreciated.
C# Code:
/// <summary>
/// Private Header template class for the DropdownCheckboxList
/// </summary>
class CheckboxListFooterTemplate : ITemplate
{
#region Public Methods
public void InstantiateIn(Control container)
{
string footer = "<input type=\"submit\" value=\"Apply\" />";
container.Controls.Add(new LiteralControl(footer));
}
#endregion Public Methods
}
/// <summary>
/// Private Header template class for the DropdownCheckboxList
/// </summary>
class CheckboxListHeaderTemplate : ITemplate
{
#region Public Methods
public void InstantiateIn(Control container)
{
string header = "<input type=\"button\" value=\"Check All\" onclick=\"CheckAll("{0}", true)\" />";
header += " <input type=\"button\" value=\"Uncheck All\" onclick=\"CheckAll("{0}", false)\" />";
container.Controls.Add(new LiteralControl(string.Format(header, container.Parent.ClientID)));
}
#endregion Public Methods
}
/// <summary>
/// Template class for the DropdownChecklistBox
/// </summary>
class CheckboxListTemplate : ITemplate
{
#region Constants
//this div will stop the list from closing as a listitem is clicked
const string head = "<div onclick=\"StopPropagation(event)\" class=\"combo-item-template\">";
const string tail = "</div>";
#endregion Constants
#region Private Methods
/// <summary>
/// Bind the data to the checkbox
/// </summary>
/// <param name="sender">Checkbox to bind data to</param>
/// <param name="e"></param>
private void checkbox_DataBinding(object sender, EventArgs e)
{
CheckBox target = (CheckBox)sender;
RadComboBoxItem item = (RadComboBoxItem)target.BindingContainer;
string itemText = (string)DataBinder.Eval(item, "Text");
target.Text = itemText;
}
#endregion Private Methods
#region Public Methods
/// <summary>
/// Create the checkbox list items in the template
/// </summary>
/// <param name="container">Container that the control will be added</param>
public void InstantiateIn(Control container)
{
CheckBox checkbox = new CheckBox();
checkbox.ID = "chkList";
checkbox.Attributes.Add("onclick", string.Format("onCheckBoxClick(this, \"{0}\")", container.Parent.ClientID));
container.Controls.Add(new LiteralControl(head));
checkbox.DataBinding += new EventHandler(checkbox_DataBinding);
container.Controls.Add(checkbox);
container.Controls.Add(new LiteralControl(tail));
}
#endregion Public Methods
}
//todo: complete summary
/// <summary>
/// based on telerik demo: http://demos.telerik.com/aspnet-ajax/combobox/examples/functionality/templates/defaultcs.aspx
/// </summary>
[DefaultProperty("Text")]
[ToolboxData("<{0}:DropdownCheckboxList runat=server></{0}:DropdownCheckboxList>")]
public class DropdownCheckboxList : RadComboBox, INamingContainer
{
#region Private Properties
string SelectedText
{
get
{
StringBuilder values = new StringBuilder(SelectedItems.Count);
foreach (RadComboBoxItem item in SelectedItems)
values.Append(item.Text + ", ");
if (values.Length > 0)
return values.ToString().Remove(values.Length - 2, 2);
else
return EmptyMessage;
}
}
#endregion Private Properties
#region Public Properties
public RadComboBoxItemCollection SelectedItems
{
get
{
CheckBox chk = null;
RadComboBoxItemCollection selectedItems = new RadComboBoxItemCollection(this);
foreach (RadComboBoxItem item in Items)
{
chk = (CheckBox)item.FindControl("chkList");
if (chk != null && chk.Checked)
selectedItems.Add(item);
}
return selectedItems;
}
}
//todo: summary
public override string SelectedValue
{
get
{
StringBuilder values = new StringBuilder(SelectedItems.Count);
foreach (RadComboBoxItem item in SelectedItems)
values.Append(item.Value + ", ");
if (values.Length > 0)
return values.ToString().Remove(values.Length - 2, 2);
else
return "";
}
set
{
if (value != null)
SelectedValues = new List<string>(value.Split(','));
}
}
//todo: summary
public List<string> SelectedValues
{
get
{
List<string> selectedValues = new List<string>();
foreach (RadComboBoxItem item in SelectedItems)
{
selectedValues.Add(item.Value);
}
return selectedValues;
}
set
{
RadComboBoxItem item = null;
CheckBox chk = null;
foreach (string val in value)
{
item = Items.FindItemByValue(val.Trim());
if (item != null)
{
chk = (CheckBox)item.FindControl("chkList");
if (chk != null)
chk.Checked = true;
}
}
}
}
#endregion Public Properties
#region Protected Methods
protected override void CreateChildControls()
{
if (base.HeaderTemplate == null)
base.HeaderTemplate = new CheckboxListHeaderTemplate();
if (base.ItemTemplate == null)
base.ItemTemplate = new CheckboxListTemplate();
if (base.FooterTemplate == null)
base.FooterTemplate = new CheckboxListFooterTemplate();
base.CreateChildControls();
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
string resourceName = "CustomControls.DropdownCheckboxList.js";
ClientScriptManager cs = this.Page.ClientScript;
cs.RegisterClientScriptResource(typeof(CustomControls.DropdownCheckboxList), resourceName);
Text = SelectedText;
}
#endregion Protected Methods
}
Javascript Code:
//based on telerik demo: http://demos.telerik.com/aspnet-ajax/combobox/examples/functionality/templates/defaultcs.aspx
var cancelDropDownClosing = false;
function StopPropagation(e) {
//cancel bubbling
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
}
function onDropDownClosing() {
cancelDropDownClosing = false;
}
function CheckAll(comboBoxId, value) {
var combo = $find(comboBoxId);
//get the collection of all items
var items = combo.get_items();
//enumerate all items
for (var i = 0; i < items.get_count(); i++) {
var item = items.getItem(i);
//get the checkbox element of the current item
var chk1 = $get(combo.get_id() + "_i" + i + "_chkList");
chk1.checked = value;
}
}
function onCheckBoxClick(chk, comboBoxId) {
var combo = $find(comboBoxId);
//holds the text of all checked items
var text = "";
//holds the values of all checked items
var values = "";
//get the collection of all items
var items = combo.get_items();
//enumerate all items
for (var i = 0; i < items.get_count(); i++) {
var item = items.getItem(i);
//get the checkbox element of the current item
var chk1 = $get(combo.get_id() + "_i" + i + "_chkList");
if (chk1.checked) {
text += item.get_text() + ", ";
values += item.get_value() + ", ";
}
}
//remove the last comma from the string
text = removeLastComma(text);
values = removeLastComma(values);
if (text.length > 0) {
//set the text of the combobox
combo.set_text(text);
}
else {
//all checkboxes are unchecked
//so reset the controls
combo.set_text("");
}
}
//this method removes the ending comma from a string
function removeLastComma(str) {
return str.replace(/,$/, "");
}