Серверный элемент управления ASP.NET на основе RadComboBox - проблема обратной передачи

Я пытаюсь создать настраиваемый элемент управления, который расширяет RadComboBox от Telerik для создания выпадающего списка флажков с шаблонами по умолчанию. План состоит в том, чтобы использовать элемент управления в нескольких местах, поэтому я хотел объединить всю логику в одном месте.

Однако у меня возникла пара странных проблем при обратной передаче. Если вы отметите несколько элементов, а затем нажмете кнопку «Применить», будут выбраны правильные элементы, но текст на флажке будет другим. Тогда при следующем постбэке я получаю ошибку 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(/,$/, "");
    }

person Community    schedule 02.11.2009    source источник


Ответы (1)


Эта строка в InstantiateIn(Control container) является основной причиной проблемы:

checkbox.ID = "chkList";

В этой строке все флажки имеют одинаковый идентификатор - они должны быть уникальными.

Так что это могло бы выглядеть примерно так

checkbox.ID = Container.ID + SomeUniqueString;

Я создал проект с предоставленным вами кодом и продублировал ошибку только с одним элементом управления на странице. (в javascript были и другие ошибки, но я смог их проигнорировать.)

Я не видел простого способа создать уникальные идентификаторы, чтобы вы знали, что они должны были делать при поиске. Итак, вместо этого:

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

Я пробовал это:

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

Это устранило ошибку и позволило мне выбрать более одного элемента. Однако этот код не идеален - он предполагает, что есть только один флажок. Лучше убедиться, что идентификаторы уникальны. Подход, который я бы выбрал, заключался бы в том, чтобы основывать идентификатор на значении элемента поля со списком.

Если выбираемые элементы фиксированы (пользователь не может добавлять новые элементы через поле со списком), вы можете вместо этого попробовать использовать RadMenu. У нас есть очень похожий элемент управления, но мы используем RadMenu. Мы просто устанавливаем изображение в пункте меню, чтобы указать статус выбора, и отслеживаем выбранные элементы в пределах нашего контроля.

person dcramos    schedule 11.11.2009