Adding groups to the ASP.NET DropDownList control

Introduction

The ASP.NET DropDownList class doesn't support option groups. (Presumably because it would break the ListItem paradigm that Microsoft has used for all their list orientated controls).

A grouped drop down list

However, it is possible to add basic option group functionality by inheriting from DropDownList and overriding the RenderContents() method.

This technique for adding optgroups is fully compatible with View State.

The code below is based on this code by Jeff Putz.

Code

public class DropDownListX : DropDownList
{
    public void AddItemGroup(string groupTitle)
    {
        this.Items.Add(new ListItem(groupTitle, "$$OPTGROUP$$OPTGROUP$$"));
    }

    protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
    {
        if (this.Items.Count > 0)
        {
            bool selected = false;
            bool optGroupStarted = false;
            for (int i = 0; i < this.Items.Count; i++)
            {
                ListItem item = this.Items[i];
                if (item.Enabled)
                {
                    if (item.Value == "$$OPTGROUP$$OPTGROUP$$")
                    {
                        if (optGroupStarted)
                            writer.WriteEndTag("optgroup");
                        writer.WriteBeginTag("optgroup");
                        writer.WriteAttribute("label", item.Text);
                        writer.Write('>');
                        writer.WriteLine();
                        optGroupStarted = true;
                    }
                    else
                    {
                        writer.WriteBeginTag("option");
                        if (item.Selected)
                        {
                            if (selected)
                            {
                                this.VerifyMultiSelect();
                            }
                            selected = true;
                            writer.WriteAttribute("selected", "selected");
                        }
                        writer.WriteAttribute("value", item.Value, true);
                        if (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("option");
                        writer.WriteLine();
                    }
                }
            }

            if (optGroupStarted)
            {
                writer.WriteEndTag("optgroup");
            }
        }
    }
}

Usage

To add the class to your project, simply copy and paste the code into a class file.

You will probably need to add the following line to the <system.web><pages><controls> section of your web.config. The line gives ASP.NET another namespace to search when it's resolving the controls in your pages. The namespace needs to match the one that you used for your DropDownListX class.

<add tagPrefix="asp" namespace="YourNameSpace.Goes.Here" />

Once that's been done you can use the control directly within an ASPX page. The control will operate exactly like a normal DropDownList, except that you can also add groups to it.

<asp:DropDownListX ID="ddlxSomething" runat="server" />

To add grouped items to the control you will need to use the inherited Items.Add() method and our new AddItemGroup() method. (Databinding will still work but then you don't have an easy way of adding the groups).

this.ddlx.AddItemGroup("Administrators");
foreach(User u in administrators)
{
    this.ddlx.Items.Add(u.Name, u.Id);
}

this.ddlx.AddItemGroup("Users");
foreach(User u in users)
{
    this.ddlx.Items.Add(u.Name, u.Id);
}

Caveats

You can't add an item with a value that matches the special group value: $$OPTGROUP$$OPTGROUP$$. Of course, if the group value isn't "unusual" enough you can always change it.