drop-down-menu - Control de lista desplegable con <optgroup> s para asp.net (webforms)?





custom-server-controls (10)


Uso el reflector para ver por qué no es compatible. Hay por qué. En el método de representación de ListControl, no hay condición para crear el grupo de selección.

protected internal override void RenderContents(HtmlTextWriter writer)
    {
        ListItemCollection items = this.Items;
        int count = items.Count;
        if (count > 0)
        {
            bool flag = false;
            for (int i = 0; i < count; i++)
            {
                ListItem item = items[i];
                if (item.Enabled)
                {
                    writer.WriteBeginTag("option");
                    if (item.Selected)
                    {
                        if (flag)
                        {
                            this.VerifyMultiSelect();
                        }
                        flag = true;
                        writer.WriteAttribute("selected", "selected");
                    }
                    writer.WriteAttribute("value", item.Value, true);
                    if (item.HasAttributes)
                    {
                        item.Attributes.Render(writer);
                    }
                    if (this.Page != null)
                    {
                        this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                    }
                    writer.Write('>');
                    HttpUtility.HtmlEncode(item.Text, writer);
                    writer.WriteEndTag("option");
                    writer.WriteLine();
                }
            }
        }
    }

Así que creo mi propio control desplegable con una anulación del método RenderContents. Hay mi control. Está funcionando bien. Utilizo exactamente el mismo código de Microsoft, solo agrego una pequeña condición para admitir listItem teniendo atributo optgroup para crear un grupo de opción y no una opción.

Dame algo de retroalimentación

public class DropDownListWithOptionGroup : DropDownList
  {
    public const string OptionGroupTag = "optgroup";
    private const string OptionTag = "option";
    protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
    {
      ListItemCollection items = this.Items;
      int count = items.Count;     
      string tag;
      string optgroupLabel;
      if (count > 0)
      {
        bool flag = false;
        for (int i = 0; i < count; i++)
        {
          tag = OptionTag;
          optgroupLabel = null;
          ListItem item = items[i];
          if (item.Enabled)
          {
            if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null)
            {
              tag = OptionGroupTag;
              optgroupLabel = item.Attributes[OptionGroupTag];
            }           
            writer.WriteBeginTag(tag);
            // NOTE(cboivin): Is optionGroup
            if (!string.IsNullOrEmpty(optgroupLabel))
            {
              writer.WriteAttribute("label", optgroupLabel);
            }
            else
            {
              if (item.Selected)
              {
                if (flag)
                {
                  this.VerifyMultiSelect();
                }
                flag = true;
                writer.WriteAttribute("selected", "selected");
              }
              writer.WriteAttribute("value", item.Value, true);
              if (item.Attributes != null && item.Attributes.Count > 0)
              {
                item.Attributes.Render(writer);
              }
              if (this.Page != null)
              {
                this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
              }
            }
            writer.Write('>');
            HttpUtility.HtmlEncode(item.Text, writer);
            writer.WriteEndTag(tag);
            writer.WriteLine();
          }
        }
      }

    }
  }

¿Alguien puede recomendar un control de lista desplegable para asp.net (3.5) que pueda representar grupos de opciones? Gracias




En función de las publicaciones anteriores, he creado una versión de CA de este control con estado de vista de trabajo.

        public const string OptionGroupTag = "optgroup";
    private const string OptionTag = "option";
    protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
    {
        ListItemCollection items = this.Items;
        int count = items.Count;
        string tag;
        string optgroupLabel;
        if (count > 0)
        {
            bool flag = false;
            for (int i = 0; i < count; i++)
            {
                tag = OptionTag;
                optgroupLabel = null;
                ListItem item = items[i];
                if (item.Enabled)
                {
                    if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null)
                    {
                        tag = OptionGroupTag;
                        optgroupLabel = item.Attributes[OptionGroupTag];
                    }
                    writer.WriteBeginTag(tag);
                    // NOTE(cboivin): Is optionGroup
                    if (!string.IsNullOrEmpty(optgroupLabel))
                    {
                        writer.WriteAttribute("label", optgroupLabel);
                    }
                    else
                    {
                        if (item.Selected)
                        {
                            if (flag)
                            {
                                this.VerifyMultiSelect();
                            }
                            flag = true;
                            writer.WriteAttribute("selected", "selected");
                        }
                        writer.WriteAttribute("value", item.Value, true);
                        if (item.Attributes != null && item.Attributes.Count > 0)
                        {
                            item.Attributes.Render(writer);
                        }
                        if (this.Page != null)
                        {
                            this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                        }
                    }
                    writer.Write('>');
                    HttpUtility.HtmlEncode(item.Text, writer);
                    writer.WriteEndTag(tag);
                    writer.WriteLine();
                }
            }
        }
    }

        protected override object SaveViewState()
    {
        object[] state = new object[this.Items.Count + 1];
        object baseState = base.SaveViewState();
        state[0] = baseState;
        bool itemHasAttributes = false;

        for (int i = 0; i < this.Items.Count; i++)
        {
            if (this.Items[i].Attributes.Count > 0)
            {
                itemHasAttributes = true;
                object[] attributes = new object[this.Items[i].Attributes.Count * 2];
                int k = 0;

                foreach (string key in this.Items[i].Attributes.Keys)
                {
                    attributes[k] = key;
                    k++;
                    attributes[k] = this.Items[i].Attributes[key];
                    k++;
                }
                state[i + 1] = attributes;
            }
        }

        if (itemHasAttributes)
            return state;
        return baseState;
    }

        protected override void LoadViewState(object savedState)
    {
        if (savedState == null)
            return;

        if (!(savedState.GetType().GetElementType() == null) &&
            (savedState.GetType().GetElementType().Equals(typeof(object))))
        {
            object[] state = (object[])savedState;
            base.LoadViewState(state[0]);

            for (int i = 1; i < state.Length; i++)
            {
                if (state[i] != null)
                {
                    object[] attributes = (object[])state[i];
                    for (int k = 0; k < attributes.Length; k += 2)
                    {
                        this.Items[i - 1].Attributes.Add
                            (attributes[k].ToString(), attributes[k + 1].ToString());
                    }
                }
            }
        }
        else
        {
            base.LoadViewState(savedState);
        }
    }

Espero que esto ayude a algunas personas :-)




Como las respuestas anteriores que sobrecargan el método RenderContents funcionan. También debe recordar alterar el estado de visualización. He entrado en un problema cuando uso viewstate no alterado en UpdatePanels. Esto tiene partes tomadas del Proyecto Sharp Pieces .

 Protected Overloads Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
    Dim list As DropDownList = Me

    Dim currentOptionGroup As String
    Dim renderedOptionGroups As New List(Of String)()

    For Each item As ListItem In list.Items
        If item.Attributes("OptionGroup") Is Nothing Then
            RenderListItem(item, writer)
        Else
            currentOptionGroup = item.Attributes("OptionGroup")

            If renderedOptionGroups.Contains(currentOptionGroup) Then
                RenderListItem(item, writer)
            Else
                If renderedOptionGroups.Count > 0 Then
                    RenderOptionGroupEndTag(writer)
                End If

                RenderOptionGroupBeginTag(currentOptionGroup, writer)
                renderedOptionGroups.Add(currentOptionGroup)

                RenderListItem(item, writer)
            End If
        End If
    Next

    If renderedOptionGroups.Count > 0 Then
        RenderOptionGroupEndTag(writer)
    End If
End Sub

Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
    writer.WriteBeginTag("optgroup")
    writer.WriteAttribute("label", name)
    writer.Write(HtmlTextWriter.TagRightChar)
    writer.WriteLine()
End Sub

Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
    writer.WriteEndTag("optgroup")
    writer.WriteLine()
End Sub

Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
    writer.WriteBeginTag("option")
    writer.WriteAttribute("value", item.Value, True)

    If item.Selected Then
        writer.WriteAttribute("selected", "selected", False)
    End If

    For Each key As String In item.Attributes.Keys
        writer.WriteAttribute(key, item.Attributes(key))
    Next

    writer.Write(HtmlTextWriter.TagRightChar)
    HttpUtility.HtmlEncode(item.Text, writer)
    writer.WriteEndTag("option")
    writer.WriteLine()
End Sub
Protected Overrides Function SaveViewState() As Object
    ' Create an object array with one element for the CheckBoxList's
    ' ViewState contents, and one element for each ListItem in skmCheckBoxList
    Dim state(Me.Items.Count + 1 - 1) As Object 'stupid vb array
    Dim baseState As Object = MyBase.SaveViewState()

    state(0) = baseState
    ' Now, see if we even need to save the view state
    Dim itemHasAttributes As Boolean = False

    For i As Integer = 0 To Me.Items.Count - 1
        If Me.Items(i).Attributes.Count > 0 Then
            itemHasAttributes = True
            ' Create an array of the item's Attribute's keys and values
            Dim attribKV(Me.Items(i).Attributes.Count * 2 - 1) As Object 'stupid vb array
            Dim k As Integer = 0
            For Each key As String In Me.Items(i).Attributes.Keys
                attribKV(k) = key
                k += 1
                attribKV(k) = Me.Items(i).Attributes(key)
                k += 1
            Next
            state(i + 1) = attribKV
        End If
    Next
    ' return either baseState or state, depending on whether or not
    ' any ListItems had attributes
    If (itemHasAttributes) Then
        Return state
    Else
        Return baseState
    End If
End Function


Protected Overrides Sub LoadViewState(ByVal savedState As Object)
    If savedState Is Nothing Then Return
    ' see if savedState is an object or object array
    If Not savedState.GetType.GetElementType() Is Nothing AndAlso savedState.GetType.GetElementType().Equals(GetType(Object)) Then

        ' we have just the base state
        MyBase.LoadViewState(savedState(0))
        'we have an array of items with attributes
        Dim state() As Object = savedState
        MyBase.LoadViewState(state(0))   '/ load the base state
        For i As Integer = 1 To state.Length - 1
            If Not state(i) Is Nothing Then
                ' Load back in the attributes
                Dim attribKV() As Object = state(i)
                For k As Integer = 0 To attribKV.Length - 1 Step +2
                    Me.Items(i - 1).Attributes.Add(attribKV(k).ToString(), attribKV(k + 1).ToString())
                Next
            End If
        Next
    Else
        'load it normal
        MyBase.LoadViewState(savedState)
    End If
End Sub






    // How to use:
    // 1. Create items in a select element or asp:DropDownList control
    // 2. Set value of an option or ListItem to "_group_", those will be converted to optgroups
    // 3. On page onload call createOptGroups(domElement), for example like this:
    //    - var lst = document.getElementById('lst');
    //    - createOptGroups(lst, "_group_");
    // 4. You can change groupMarkerValue to anything, I used "_group_"
    function createOptGroups(lst, groupMarkerValue) {
        // Get an array containing the options
        var childNodes = [];
        for (var i = 0; i < lst.options.length; i++)
            childNodes.push(lst.options[i]);

        // Get the selected element so we can preserve selection
        var selectedIndex = lst.selectedIndex;
        var selectedChild = childNodes[selectedIndex];
        var selectedValue = selectedChild.value;

        // Remove all elements
        while (lst.hasChildNodes())
            lst.removeChild(lst.childNodes[0]);

        // Go through the array of options and convert some into groups
        var group = null;
        for (var i = 0; i < childNodes.length; i++) {
            var node = childNodes[i];
            if (node.value == groupMarkerValue) {
                group = document.createElement("optgroup");
                group.label = node.text;
                group.value = groupMarkerValue;
                lst.appendChild(group);
                continue;
            }

            // Add to group or directly under list
            (group == null ? lst : group).appendChild(node);
        }

        // Preserve selected, no support for multi-selection here, sorry
        selectedChild.selected = true;
    }

Probado en Chrome 16, Firefox 9 e IE8.




Un enfoque más genérico para Irfan's solución basada en jQuery Irfan's :

backend

private void _addSelectItem(DropDownList list, string title, string value, string group = null) {
   ListItem item = new ListItem(title, value);
   if (!String.IsNullOrEmpty(group))
   {
       item.Attributes["data-category"] = group;
   }
   list.Items.Add(item);
}

...
_addSelectItem(dropDown, "Option 1", "1");
_addSelectItem(dropDown, "Option 2", "2", "Category");
_addSelectItem(dropDown, "Option 3", "3", "Category");
...

cliente

var groups = {};
$("select option[data-category]").each(function () {
     groups[$.trim($(this).attr("data-category"))] = true;
});
$.each(groups, function (c) {
     $("select option[data-category='"+c+"']").wrapAll('<optgroup label="' + c + '">');
});



Lo he hecho usando un repetidor externo para los grupos de selección y optgroups y un repetidor interno para los elementos dentro de ese grupo:

<asp:Repeater ID="outerRepeater" runat="server">
    <HeaderTemplate>
        <select id="<%= outerRepeater.ClientID %>">
    </HeaderTemplate>
    <ItemTemplate>
            <optgroup label="<%# Eval("GroupText") %>">
                <asp:Repeater runat="server" DataSource='<%# Eval("Items") %>'>
                    <ItemTemplate>
                        <option value="<%# Eval("Value") %>"><%# Eval("Text") %></option>
                    </ItemTemplate>
                </asp:Repeater>
            </optgroup>
    </ItemTemplate>
    <FooterTemplate>
        </select>
    </FooterTemplate>
</asp:Repeater>

La fuente de datos para outerRepeater es una agrupación simple de la siguiente manera:

var data = (from o in thingsToDisplay
            group oby GetAlphaGrouping(o.Name) into g
            orderby g.Key
            select new
            {
                Alpha = g.Key,
                Items = g
            });

Y para obtener el carácter de agrupación alfa:

private string GetAlphaGrouping(string value)
{
    string firstChar = value.Substring(0, 1).ToUpper();
    int unused;

    if (int.TryParse(firstChar, out unused))
        return "#";

    return firstChar.ToUpper();
}

No es una solución perfecta, pero funciona. La solución correcta sería dejar de usar WebForms, pero usaremos MVC. :)




He usado JQuery para lograr esta tarea. Primero agregué un nuevo atributo para cada ListItem desde el backend y luego usé ese atributo en el wrapAll() JQuery wrapAll() para crear grupos ...

DO#:

foreach (ListItem item in ((DropDownList)sender).Items)
{
    if (System.Int32.Parse(item.Value) < 5)
        item.Attributes.Add("classification", "LessThanFive");
    else
        item.Attributes.Add("classification", "GreaterThanFive");
} 

JQuery:

$(document).ready(function() {
    //Create groups for dropdown list
    $("select.listsmall option[@classification='LessThanFive']")
        .wrapAll("&lt;optgroup label='Less than five'&gt;");
    $("select.listsmall option[@classification='GreaterThanFive']")
        .wrapAll("&lt;optgroup label='Greater than five'&gt;"); 
});



El código anterior representa la etiqueta final para el grupo de opciones antes de cualquiera de las opciones, por lo que las opciones no se sangran como deberían, además del marcado que no representa correctamente la agrupación. Aquí está mi versión ligeramente modificada del código de Tom:

    public class ExtendedDropDownList : System.Web.UI.WebControls.DropDownList
{
    public const string OptionGroupTag = "optgroup";
    private const string OptionTag = "option";
    protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
    {
        ListItemCollection items = this.Items;
        int count = items.Count;
        string tag;
        string optgroupLabel;
        if (count > 0)
        {
            bool flag = false;
            string prevOptGroup = null;
            for (int i = 0; i < count; i++)
            {
                tag = OptionTag;
                optgroupLabel = null;
                ListItem item = items[i];
                if (item.Enabled)
                {
                    if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null)
                    {
                        optgroupLabel = item.Attributes[OptionGroupTag];

                        if (prevOptGroup != optgroupLabel)
                        {
                            if (prevOptGroup != null)
                            {
                                writer.WriteEndTag(OptionGroupTag);
                            }
                            writer.WriteBeginTag(OptionGroupTag);
                            if (!string.IsNullOrEmpty(optgroupLabel))
                            {
                                writer.WriteAttribute("label", optgroupLabel);
                            }
                            writer.Write('>');
                        }
                        item.Attributes.Remove(OptionGroupTag);
                        prevOptGroup = optgroupLabel;
                    }
                    else
                    {
                        if (prevOptGroup != null)
                        {
                            writer.WriteEndTag(OptionGroupTag);
                        }
                        prevOptGroup = null;
                    }

                    writer.WriteBeginTag(tag);
                    if (item.Selected)
                    {
                        if (flag)
                        {
                            this.VerifyMultiSelect();
                        }
                        flag = true;
                        writer.WriteAttribute("selected", "selected");
                    }
                    writer.WriteAttribute("value", item.Value, true);
                    if (item.Attributes != null && item.Attributes.Count > 0)
                    {
                        item.Attributes.Render(writer);
                    }
                    if (optgroupLabel != null)
                    {
                        item.Attributes.Add(OptionGroupTag, optgroupLabel);
                    }
                    if (this.Page != null)
                    {
                        this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                    }

                    writer.Write('>');
                    HttpUtility.HtmlEncode(item.Text, writer);
                    writer.WriteEndTag(tag);
                    writer.WriteLine();
                    if (i == count - 1)
                    {
                        if (prevOptGroup != null)
                        {
                            writer.WriteEndTag(OptionGroupTag);
                        }
                    }
                }
            }
        }
    }

    protected override object SaveViewState()
    {
        object[] state = new object[this.Items.Count + 1];
        object baseState = base.SaveViewState();
        state[0] = baseState;
        bool itemHasAttributes = false;

        for (int i = 0; i < this.Items.Count; i++)
        {
            if (this.Items[i].Attributes.Count > 0)
            {
                itemHasAttributes = true;
                object[] attributes = new object[this.Items[i].Attributes.Count * 2];
                int k = 0;

                foreach (string key in this.Items[i].Attributes.Keys)
                {
                    attributes[k] = key;
                    k++;
                    attributes[k] = this.Items[i].Attributes[key];
                    k++;
                }
                state[i + 1] = attributes;
            }
        }

        if (itemHasAttributes)
            return state;
        return baseState;
    }

    protected override void LoadViewState(object savedState)
    {
        if (savedState == null)
            return;

        if (!(savedState.GetType().GetElementType() == null) &&
            (savedState.GetType().GetElementType().Equals(typeof(object))))
        {
            object[] state = (object[])savedState;
            base.LoadViewState(state[0]);

            for (int i = 1; i < state.Length; i++)
            {
                if (state[i] != null)
                {
                    object[] attributes = (object[])state[i];
                    for (int k = 0; k < attributes.Length; k += 2)
                    {
                        this.Items[i - 1].Attributes.Add
                            (attributes[k].ToString(), attributes[k + 1].ToString());
                    }
                }
            }
        }
        else
        {
            base.LoadViewState(savedState);
        }
    }
}

Úselo así:

            ListItem item1 = new ListItem("option1");
        item1.Attributes.Add("optgroup", "CatA");
        ListItem item2 = new ListItem("option2");
        item2.Attributes.Add("optgroup", "CatA");
        ListItem item3 = new ListItem("option3");
        item3.Attributes.Add("optgroup", "CatB");
        ListItem item4 = new ListItem("option4");
        item4.Attributes.Add("optgroup", "CatB");
        ListItem item5 = new ListItem("NoOptGroup");

        ddlTest.Items.Add(item1);
        ddlTest.Items.Add(item2);
        ddlTest.Items.Add(item3);
        ddlTest.Items.Add(item4);
        ddlTest.Items.Add(item5);

y aquí está el marcado generado (sangrado para facilitar su visualización):

 <select name="ddlTest" id="Select1">
    <optgroup label="CatA">
        <option selected="selected" value="option1">option1</option>
        <option value="option2">option2</option>
    </optgroup>
    <optgroup label="CatB">
        <option value="option3">option3</option>
        <option value="option4">option4</option>
    </optgroup>
    <option value="NoOptGroup">NoOptGroup</option>
 </select>



Para ordenar un origen de datos de objeto que devuelve un conjunto de datos, utiliza la propiedad Ordenar del control.

Ejemplo de uso En la página aspx para ordenar por orden ascendente de ColumnName

<asp:ObjectDataSource ID="dsData" runat="server" TableName="Data" 
 Sort="ColumnName ASC" />




asp.net drop-down-menu custom-server-controls webforms