web-dev-qa-db-ja.com

asp.net(webforms)の<optgroup> sを使用したドロップダウンリストコントロール?

オプショングループをレンダリングできるasp.net(3.5)のドロップダウンリストコントロールを誰でも推奨できますか?ありがとう

64
Nick

過去に標準コントロールを使用しましたが、特定の場所で<optgroup>をレンダリングできるように、デフォルトの動作をオーバーライドする単純な ControlAdapter を追加しました。これは、追加機能が邪魔にならないため、特別な動作を必要としないコントロールがあってもうまく機能します。

これは特定の目的のためであり、.Net 2.0で書かれているため、あなたにも似合わないかもしれませんが、少なくとも出発点を与える必要があります。また、プロジェクトで.browserfileを使用して接続する必要があります(例については投稿の最後を参照してください)。

'This codes makes the dropdownlist control recognize items with "--"
'for the label or items with an OptionGroup attribute and render them
'as <optgroup> instead of <option>.
Public Class DropDownListAdapter
    Inherits System.Web.UI.WebControls.Adapters.WebControlAdapter

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim list As DropDownList = Me.Control
        Dim currentOptionGroup As String
        Dim renderedOptionGroups As New Generic.List(Of String)

        For Each item As ListItem In list.Items
            Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value)
            If item.Attributes("OptionGroup") IsNot Nothing Then
                'The item is part of an option group
                currentOptionGroup = item.Attributes("OptionGroup")
                If Not renderedOptionGroups.Contains(currentOptionGroup) Then
                    'the header was not written- do that first
                    'TODO: make this stack-based, so the same option group can be used more than once in longer select element (check the most-recent stack item instead of anything in the list)
                    If (renderedOptionGroups.Count > 0) Then
                        RenderOptionGroupEndTag(writer) 'need to close previous group
                    End If
                    RenderOptionGroupBeginTag(currentOptionGroup, writer)
                    renderedOptionGroups.Add(currentOptionGroup)
                End If
                RenderListItem(item, writer)
            ElseIf item.Text = "--" Then 'simple separator
                RenderOptionGroupBeginTag("--", writer)
                RenderOptionGroupEndTag(writer)
            Else
                'default behavior: render the list item as normal
                RenderListItem(item, writer)
            End If
        Next item

        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 key

        writer.Write(HtmlTextWriter.TagRightChar)
        HttpUtility.HtmlEncode(item.Text, writer)
        writer.WriteEndTag("option")
        writer.WriteLine()
    End Sub
End Class

同じクラスのC#実装を次に示します。

/* This codes makes the dropdownlist control recognize items with "--"
 * for the label or items with an OptionGroup attribute and render them
 * as <optgroup> instead of <option>.
 */
public class DropDownListAdapter : WebControlAdapter
{
    protected override void RenderContents(HtmlTextWriter writer)
    {
        //System.Web.HttpContext.Current.Response.Write("here");
        var list = (DropDownList)this.Control;
        string currentOptionGroup;
        var renderedOptionGroups = new List<string>();

        foreach (ListItem item in list.Items)
        {
            Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value);
            //Is the item part of an option group?
            if (item.Attributes["OptionGroup"] != null)
            {
                currentOptionGroup = item.Attributes["OptionGroup"];
                //Was the option header already written, then just render the list item
                if (renderedOptionGroups.Contains(currentOptionGroup))
                    RenderListItem(item, writer);
                //The header was not written,do that first
                else
                {
                    //Close previous group
                    if (renderedOptionGroups.Count > 0)
                        RenderOptionGroupEndTag(writer);

                    RenderOptionGroupBeginTag(currentOptionGroup, writer);
                    renderedOptionGroups.Add(currentOptionGroup);
                    RenderListItem(item, writer);
                }
            }
            //Simple separator
            else if (item.Text == "--")
            {
                RenderOptionGroupBeginTag("--", writer);
                RenderOptionGroupEndTag(writer);
            }
            //Default behavior, render the list item as normal
            else
                RenderListItem(item, writer);
        }

        if (renderedOptionGroups.Count > 0)
            RenderOptionGroupEndTag(writer);
    }

    private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
    {
        writer.WriteBeginTag("optgroup");
        writer.WriteAttribute("label", name);
        writer.Write(HtmlTextWriter.TagRightChar);
        writer.WriteLine();
    }
    private void RenderOptionGroupEndTag(HtmlTextWriter writer)
    {
        writer.WriteEndTag("optgroup");
        writer.WriteLine();
    }
    private void RenderListItem(ListItem item, HtmlTextWriter writer)
    {
        writer.WriteBeginTag("option");
        writer.WriteAttribute("value", item.Value, true);
        if (item.Selected)
            writer.WriteAttribute("selected", "selected", false);

        foreach (string key in item.Attributes.Keys)
            writer.WriteAttribute(key, item.Attributes[key]);

        writer.Write(HtmlTextWriter.TagRightChar);
        HttpUtility.HtmlEncode(item.Text, writer);
        writer.WriteEndTag("option");
        writer.WriteLine();
    }
}

ブラウザファイルの名前は「App_Browsers\BrowserFile.browser」で、次のようになりました。

<!--
    You can find existing browser definitions at
    <windir>\Microsoft.NET\Framework\<ver>\CONFIG\Browsers
-->
<browsers>
   <browser refID="Default">
      <controlAdapters>
        <adapter controlType="System.Web.UI.WebControls.DropDownList" 
               adapterType="DropDownListAdapter" />
      </controlAdapters>
   </browser>
</browsers>
47
Joel Coehoorn

JQueryを使用してこのタスクを達成しました。最初にバックエンドからのListItemごとに新しい属性を追加し、JQuery wrapAll()メソッドでその属性を使用してグループを作成しました...

C#:

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;"); 
});
25
Irfan

ジョエルありがとう!皆さん...必要に応じてC#バージョンをご覧ください:



using System;
using System.Web.UI.WebControls.Adapters;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Web;

//This codes makes the dropdownlist control recognize items with "--"'
//for the label or items with an OptionGroup attribute and render them'
//as  instead of .'
public class DropDownListAdapter : WebControlAdapter
{

    protected override void RenderContents(HtmlTextWriter writer)
    {
        DropDownList list = Control as DropDownList;
        string currentOptionGroup;
        List renderedOptionGroups = new List();

        foreach(ListItem item in list.Items)
        {
            if (item.Attributes["OptionGroup"] != null)
            {
                //'The item is part of an option group'
                currentOptionGroup = item.Attributes["OptionGroup"];
                //'the option header was already written, just render the list item'
                if(renderedOptionGroups.Contains(currentOptionGroup))
                    RenderListItem(item, writer);
                else
                {
                    //the header was not written- do that first'
                    if (renderedOptionGroups.Count > 0)
                        RenderOptionGroupEndTag(writer); //'need to close previous group'
                    RenderOptionGroupBeginTag(currentOptionGroup, writer);
                    renderedOptionGroups.Add(currentOptionGroup);
                    RenderListItem(item, writer);
                }
            }
            else if (item.Text == "--") //simple separator
            {
                RenderOptionGroupBeginTag("--", writer);
                RenderOptionGroupEndTag(writer);
            }
            else
            {
                //default behavior: render the list item as normal'
                RenderListItem(item, writer);
            }
        }

        if(renderedOptionGroups.Count > 0)
            RenderOptionGroupEndTag(writer);
    }

    private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
    {
        writer.WriteBeginTag("optgroup");
        writer.WriteAttribute("label", name);
        writer.Write(HtmlTextWriter.TagRightChar);
        writer.WriteLine();
    }

    private void RenderOptionGroupEndTag(HtmlTextWriter writer)
    {
        writer.WriteEndTag("optgroup");
        writer.WriteLine();
    }

    private void RenderListItem(ListItem item, HtmlTextWriter writer)
    {
        writer.WriteBeginTag("option");
        writer.WriteAttribute("value", item.Value, true);
        if (item.Selected)
            writer.WriteAttribute("selected", "selected", false);


        foreach (string key in item.Attributes.Keys)
            writer.WriteAttribute(key, item.Attributes[key]);

        writer.Write(HtmlTextWriter.TagRightChar);
        HttpUtility.HtmlEncode(item.Text, writer);
        writer.WriteEndTag("option");
        writer.WriteLine();
    }

}



20

上記のコードは、オプションの前にoptgroupの終了タグをレンダリングするので、グループ化を適切に表していないマークアップに加えて、オプションがインデントされる必要がありません。これが、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);
        }
    }
}

次のように使用します。

            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);

生成されたマークアップは次のとおりです(見やすくするためにインデントされています)。

 <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>
18
nw.

CodePlexのSharp Piecesプロジェクト は、この(および他のいくつかの)制御の制限を解決します。

6

リフレクターを使用して、サポートされていない理由を確認します。理由があります。 ListControlのrenderメソッドでは、optgroupを作成するための条件はありません。

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();
                }
            }
        }
    }

そのため、メソッドRenderContentsをオーバーライドして独自のドロップダウンコントロールを作成します。私のコントロールがあります。正常に動作しています。私はMicrosoftのまったく同じコードを使用します。オプションではなくoptgroupを作成する属性optgroupを持つlistItemをサポートするために、少し条件を追加するだけです。

フィードバックをください

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();
          }
        }
      }

    }
  }
6
Cédric Boivin

上記の投稿に基づいて、作業ビューステートでこのコントロールのc#バージョンを作成しました。

        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);
        }
    }

私はこれが一部の人々に役立つことを願っています:-)

5
Tom Miller

Irfan's jQueryベースのソリューションへのより一般的なアプローチ:

バックエンド

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");
...

クライアント

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 + '">');
});
5
mhu

Selectおよびoptgroupsには外側のリピーターを使用し、そのグループ内のアイテムには内側のリピーターを使用してこれを実行しました。

<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>

outerRepeaterのデータソースは、次のような単純なグループ化です。

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

そして、アルファグループ化文字を取得するには:

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

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

    return firstChar.ToUpper();
}

これは完璧なソリューションではありませんが、機能します。 正しい解決策は、WebFormsを使用しないことですが、代わりにMVCを使用します。 :)

2
Josh M.
    // 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;
    }

テスト済みChrome 16、Firefox 9およびIE8。

1
Tommy

上記の回答がオーバーロードしているように、RenderContentsメソッドは機能します。また、ビューステートを変更することを忘れないでください。 UpdatePanelsで変更されていないビューステートを使用すると問題が発生しました。これには Sharp Pieces Project から取られた部分があります。

 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
1
Glennular