基于RadComboBox的ASP.NET服务器控件 - 回发问题

时间:2009-11-02 19:10:43

标签: asp.net controls postback telerik

我正在尝试创建一个自定义控件,从Telerik扩展RadComboBox以使用默认模板创建下拉复选框列表。计划是在几个地方使用控件,所以我想在一个地方合并所有逻辑。

然而,我在回发时遇到了一些奇怪的问题。如果您检查了几个项目然后点击“应用”按钮,则会选择正确的项目,但复选框上的文本会有所不同。然后在下一个回发中我收到错误Multiple controls with the same ID 'i2' were found. FindControl requires that controls have unique IDs.

附加是自定义控件。任何帮助表示赞赏。

C#代码:

/// <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(&quot;{0}&quot;, true)\" />";
        header += "&nbsp;<input type=\"button\" value=\"Uncheck All\" onclick=\"CheckAll(&quot;{0}&quot;, 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代码:

    //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(/,$/, "");
    }

1 个答案:

答案 0 :(得分:0)

InstantiateIn(Control container)中的这一行是问题的主要原因:

checkbox.ID = "chkList";

这一行使每个复选框都具有相同的ID - 它们应该是唯一的。

所以看起来可能更像是

checkbox.ID = Container.ID + SomeUniqueString;

我使用您提供的代码创建了一个项目,并在页面上只有一个控件复制了错误。 (javascript中还有其他错误,但我能够忽略它们。)

我看不到创建唯一ID的简单方法,以便您可以知道他们要做什么。所以不要这样:

chk = (CheckBox)item.FindControl("chkList");

我试过了:

foreach (var o in item.Controls)
{
   if (o is CheckBox)
   {
      chk = (CheckBox) o;
   }
} 

它消除了错误并允许我选择多个项目。但是,此代码并不理想 - 它假设只有一个复选框。你最好确保id是唯一的。我将采用的方法是将id基于组合框项的值。

如果修改了可选项(用户无法通过组合框添加新项),则可以尝试使用RadMenu。我们有一个非常类似的控件,但我们使用RadMenu。我们只需在菜单项上设置图像以指示选择状态,我们就会跟踪控制范围内的所选项目。