Click here to Skip to main content
15,885,278 members
Articles / Web Development / ASP.NET
Article

ASP.NET DropDownList with OptionGroup support

Rate me:
Please Sign up or sign in to vote.
4.81/5 (39 votes)
12 Sep 20061 min read 485.9K   8.7K   108   100
An implementation of a DropDownList Control Adapter which provides OptionGroup support.

Introduction

ASP.NET 2.0, for all its bells and whistles, lacks the odd bit of functionality for reasons completely unknown. One such notable omission is that of OptionGroup (<optgroup>) support in the DropDownList control. For those unfamiliar with the <optgroup> element, it is part of the XHTML standard, and has the effect of categorising items in a <select>, as the following image shows.

Option Groups

When I first began to look for a solution to implement this in ASP.NET, I found very few articles on the topic that offered any viable solution. Another CodeProject member came up with a nice clean solution, but it looked like a lot of code and I was convinced there was an easier way. After reading some comments, it became apparent a Control Adapter was the way to go. Control Adapters are new in ASP.NET 2.0, and allow the developer to override the rendering behaviour of any control, very powerful stuff! Furthermore, Control Adapters are used in conjunction with a browser file, so specific browsers may be targeted, if required. Armed with that knowledge, the solution became simple. The attached download contains the requisite files to implement this solution in your own projects, but for posterity, I paste it here also.

C#
public class DropDownListAdapter : 
       System.Web.UI.WebControls.Adapters.WebControlAdapter {
    protected override void RenderContents(HtmlTextWriter writer) {
        DropDownList list = this.Control as DropDownList;
        string currentOptionGroup;
        List<string> renderedOptionGroups = new List<string>();
        foreach(ListItem item in list.Items) {
            if(item.Attributes["OptionGroup"] == null) {
                RenderListItem(item, writer);
            } else {
                currentOptionGroup = item.Attributes["OptionGroup"];
                if(renderedOptionGroups.Contains(currentOptionGroup)) {
                    RenderListItem(item, writer);
                } else {
                    if(renderedOptionGroups.Count > 0) {
                        RenderOptionGroupEndTag(writer);
                    }
                    RenderOptionGroupBeginTag(currentOptionGroup, 
                                              writer);
                    renderedOptionGroups.Add(currentOptionGroup);
                    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();
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: change of Font Style optgroup element Pin
lotuspro23-Aug-07 23:06
lotuspro23-Aug-07 23:06 
GeneralRe: change of Font Style optgroup element Pin
Nayeem Yahoo16-Jan-09 2:24
Nayeem Yahoo16-Jan-09 2:24 
GeneralRe: change of Font Style optgroup element Pin
GilesHinton2-Aug-10 5:46
GilesHinton2-Aug-10 5:46 
GeneralUpdate to RenderContents Pin
Pedro Maia Costa20-Jul-07 1:05
Pedro Maia Costa20-Jul-07 1:05 
QuestionList get added again after postback Pin
xsoftdev19-Jul-07 5:20
xsoftdev19-Jul-07 5:20 
AnswerRe: List get added again after postback Pin
Pedro Maia Costa20-Jul-07 1:07
Pedro Maia Costa20-Jul-07 1:07 
GeneralVB version anyone... Pin
jaywoncoder10-May-07 12:46
jaywoncoder10-May-07 12:46 
AnswerVB version with fixes below... Pin
WolfyUK20-Jun-07 5:38
WolfyUK20-Jun-07 5:38 
...and a couple of other minor alterations:

Imports System
Imports System.Data
Imports System.Configuration
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
Imports System.Collections
Imports System.Collections.Generic

Public Class DropDownListAdapter
Inherits System.Web.UI.WebControls.Adapters.WebControlAdapter

Private Const m_optionGroupAttribute As String = "OptionGroup"
Private Const m_tagOptionGroup As String = "optgroup"
Private Const m_attributeLabel As String = "label"
Private m_viewstates As Object()

Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
If (Not Page Is Nothing) Then
Dim l_list As DropDownList = CType(Me.Control, DropDownList)
Dim l_currentOptionGroup As String
Dim l_renderedOptionGroups As New ArrayList()

For Each l_item As ListItem In l_list.Items
Page.ClientScript.RegisterForEventValidation(l_list.UniqueID, l_item.Value)

If l_item.Attributes(m_optionGroupAttribute) Is Nothing Then
RenderListItem(l_item, writer)
Else
l_currentOptionGroup = l_item.Attributes(m_optionGroupAttribute)

If (l_renderedOptionGroups.Contains(l_currentOptionGroup)) Then
RenderListItem(l_item, writer)
Else
If (l_renderedOptionGroups.Count > 0) Then
RenderOptionGroupEndTag(writer)
End If
RenderOptionGroupBeginTag(l_currentOptionGroup, writer)
l_renderedOptionGroups.Add(l_currentOptionGroup)
RenderListItem(l_item, writer)
End If
End If
Next

If (l_renderedOptionGroups.Count > 0) Then
RenderOptionGroupEndTag(writer)
End If
Else
MyBase.RenderContents(writer)
End If
End Sub

Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
writer.AddAttribute(m_attributeLabel, name)
writer.RenderBeginTag(m_tagOptionGroup)
End Sub

Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
writer.RenderEndTag()
End Sub

Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
For Each key As String In item.Attributes.Keys
If (key <> m_optionGroupAttribute) Then
writer.AddAttribute(key, item.Attributes(key))
End If
Next

writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value, True)

If (item.Selected) Then
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected")
End If

writer.RenderBeginTag(HtmlTextWriterTag.Option)
writer.WriteEncodedText(item.Text)
writer.RenderEndTag()
End Sub

Protected Overrides Function SaveAdapterViewState() As Object
If (Not Page Is Nothing) Then
Dim l_list As DropDownList = CType(Control, DropDownList)
Dim l_viewState(l_list.Items.Count + 1) As Object

Dim i As Integer = 0
For Each item As ListItem In l_list.Items

l_viewState(i) = item.Attributes(m_optionGroupAttribute)
i += 1
Next

l_viewState(i) = MyBase.SaveAdapterViewState()
Return l_viewState
Else
Return MyBase.SaveAdapterControlState()
End If
End Function

Protected Overrides Sub LoadAdapterViewState(ByVal state As Object)
If (Not Page Is Nothing) Then
m_viewstates = CType(state, Object())
MyBase.LoadAdapterViewState(m_viewstates(m_viewstates.Length - 1))
Else
MyBase.LoadAdapterViewState(state)
End If
End Sub

Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
If (Not Page Is Nothing) Then
If (Not m_viewstates Is Nothing AndAlso m_viewstates.Length > 1) Then
Dim l_list As DropDownList = CType(Control, DropDownList)
If (Page.EnableEventValidation) Then
If (m_viewstates.Length <> l_list.Items.Count + 1) Then
Throw New ViewStateException()
End If
End If

Dim l_max As Integer = m_viewstates.Length
If (l_list.Items.Count < l_max) Then
l_max = l_list.Items.Count
End If

For i As Integer = 0 To l_max - 1
l_list.Items(i).Attributes(m_optionGroupAttribute) = CStr(m_viewstates(i))
Next
End If
End If

MyBase.OnPreRender(e)
End Sub
End Class

Marc
AnswerStandalone version [modified] Pin
Paul Appeldoorn4-Oct-07 3:53
Paul Appeldoorn4-Oct-07 3:53 
GeneralRe: Standalone version Pin
Member 437800431-Jul-08 20:18
Member 437800431-Jul-08 20:18 
QuestionRe: VB version with fixes below... [modified] Pin
CaptainRantflaps12-Nov-08 23:15
CaptainRantflaps12-Nov-08 23:15 
QuestionAjax [modified] Pin
davidprice3520-Mar-07 5:09
davidprice3520-Mar-07 5:09 
QuestionCould Not Load Type 'DropDownListAdapter' Pin
tinybag1615-Mar-07 17:09
tinybag1615-Mar-07 17:09 
AnswerRe: Could Not Load Type 'DropDownListAdapter' Pin
lotuspro16-Mar-07 1:50
lotuspro16-Mar-07 1:50 
AnswerRe: Could Not Load Type 'DropDownListAdapter' Pin
crockera11-Sep-07 23:57
crockera11-Sep-07 23:57 
AnswerRe: Could Not Load Type 'DropDownListAdapter' Pin
AndrewR736-Nov-07 8:56
AndrewR736-Nov-07 8:56 
QuestionOther browsers Pin
venetaibird18-Oct-06 19:06
venetaibird18-Oct-06 19:06 
AnswerRe: Other browsers Pin
lotuspro19-Oct-06 4:07
lotuspro19-Oct-06 4:07 
GeneralRe: Other browsers Pin
scott091215-Feb-07 4:55
scott091215-Feb-07 4:55 
AnswerRe: Other browsers Pin
lotuspro15-Feb-07 5:17
lotuspro15-Feb-07 5:17 
GeneralRe: Other browsers Pin
jnovax3-Jul-07 23:31
jnovax3-Jul-07 23:31 
QuestionenableEventValidation in PostBack ? Pin
itzikbs19-Sep-06 23:53
itzikbs19-Sep-06 23:53 
AnswerRe: enableEventValidation in PostBack ? [modified] Pin
Thxman25-Sep-06 3:16
Thxman25-Sep-06 3:16 
GeneralRe: enableEventValidation in PostBack ? Pin
lotuspro25-Sep-06 22:18
lotuspro25-Sep-06 22:18 
GeneralRe: enableEventValidation in PostBack ? Pin
Thxman27-Sep-06 2:23
Thxman27-Sep-06 2:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.