It is a well known fact that the asp.net DropdownList control is lacking a very specific feature: OPTGROUP element support.

Here's a little something to add that functionality, with a drop-in assembly and a .browser file.

  1. Create a project named DropDownOptGroups (or whatever you want)
  2. Create a class, named DropDownListAdapter, inheriting from System.Web.UI.WebControls.Adapters.WebControlAdapter
  3. Add the following code to your class:
      public class DropDownListAdapter : WebControlAdapter    {
            protected override void RenderContents(HtmlTextWriter writer)
            {
                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 (!string.IsNullOrEmpty(item.Attributes["OptionGroup"]))
                    {
                        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();
            }
        }
  4. Build this project, get the resulting dll, put it in your main project's bin folder.
  5. Add the App_Browsers folder to your main project (if you don't already have it in place)
  6. Add the following file, named OptGroup.browser in the App_Browser folder:
    <browsers>
      <browser refID="Default">
        <controlAdapters>
          <adapter controlType="System.Web.UI.WebControls.DropDownList" 
                 adapterType="DropDownOptGroups.DropDownListAdapter, DropDownOptGroups" />
        </controlAdapters>
      </browser>
    </browsers>
  7. Make sure you adjust namespaces, assembly names and class names to match what you have coded.
  8. Now, in your main project, if you want to have a dropdownlist with optgroups, you use the good ole' DropdownList in your markup and add a custom "OptionGroup" attribute to all List items that you want under an option group. Something like the following:
    <asp:DropDownList ID="ddlWithOptGroup" runat="server">
        <asp:ListItem Text="Test - 0" Value="0"></asp:ListItem>
        <asp:ListItem Text="Test - 1" Value="1"></asp:ListItem>
        <asp:ListItem Text="Test - 2" Value="2" OptionGroup="Even"></asp:ListItem>
        <asp:ListItem Text="Test - 4" Value="4" OptionGroup="Even"></asp:ListItem>
        <asp:ListItem Text="Test - 6" Value="6" OptionGroup="Even"></asp:ListItem>
        <asp:ListItem Text="Test - 3" Value="3" OptionGroup="Odd"></asp:ListItem>
        <asp:ListItem Text="Test - 5" Value="5" OptionGroup="Odd"></asp:ListItem>
    </asp:DropDownList>
  9. The result is as follows:

Happy coding!