I have a requirement of grouping the drop down list options in ASP.NET drop down server control. Do you have any idea to how to approach the issue? I am new to ASP.NET.
-
3Interesting, the very print you posted is from an article explaining how to do that in asp.net dropdown list control. Doesn't it work? – Conrad Clark Apr 23 '13 at 11:13
-
2The site you got the image from explains it http://ignatu.co.uk/articles/Adding_groups_to_the_ASPNET_DropDownList_control/ – keyboardP Apr 23 '13 at 11:14
-
2See this Article [Group options in DropDownList](http://weblogs.asp.net/alaaalnajjar/archive/2009/11/16/group-options-in-dropdownlist.aspx) and this [Dropdownlist control with – Maryam Arshi Apr 23 '13 at 11:15
-
There is some links that will explain you better.Please follow [Link One](http://ignatu.co.uk/articles/Adding_groups_to_the_ASPNET_DropDownList_control/) [Link Two](http://weblogs.asp.net/ricardoperes/archive/2013/02/02/asp-net-dropdownlist-with-groups.aspx) [Link Three](http://stackoverflow.com/questions/130020/dropdownlist-control-with-optgroups-for-asp-net-webforms) Hope it helps for you. – Rahul Apr 23 '13 at 11:16
10 Answers
Check out this article, I too had need for Group DropDown list . . .
ASP.NET DropDownList with OptionGroup support
Usage :
protected void Page_Load(object sender, EventArgs e)
{
ListItem item1 = new ListItem("Camel", "1");
item1.Attributes["OptionGroup"] = "Mammals";
ListItem item2 = new ListItem("Lion", "2");
item2.Attributes["OptionGroup"] = "Mammals";
ListItem item3 = new ListItem("Whale", "3");
item3.Attributes["OptionGroup"] = "Mammals";
ListItem item4 = new ListItem("Walrus", "4");
item4.Attributes["OptionGroup"] = "Mammals";
ListItem item5 = new ListItem("Velociraptor", "5");
item5.Attributes["OptionGroup"] = "Dinosaurs";
ListItem item6 = new ListItem("Allosaurus", "6");
item6.Attributes["OptionGroup"] = "Dinosaurs";
ListItem item7 = new ListItem("Triceratops", "7");
item7.Attributes["OptionGroup"] = "Dinosaurs";
ListItem item8 = new ListItem("Stegosaurus", "8");
item8.Attributes["OptionGroup"] = "Dinosaurs";
ListItem item9 = new ListItem("Tyrannosaurus", "9");
item9.Attributes["OptionGroup"] = "Dinosaurs";
ddlItems.Items.Add(item1);
ddlItems.Items.Add(item2);
ddlItems.Items.Add(item3);
ddlItems.Items.Add(item4);
ddlItems.Items.Add(item5);
ddlItems.Items.Add(item6);
ddlItems.Items.Add(item7);
ddlItems.Items.Add(item8);
ddlItems.Items.Add(item9);
}

- 20,189
- 9
- 73
- 110
-
3One note about this approach is while it works you have to implement custom view state logic to save the attributes and restore them after postback. [How to maintain the attributes](https://stackoverflow.com/questions/1313447/listitems-attributes-in-a-dropdownlist-are-lost-on-postback) – Aaron Jul 24 '17 at 04:07
I really like this client-side solution (doesn't need a custom DropDownList, but uses jQuery):
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");
...
client
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 + '">');
});

- 17,720
- 10
- 62
- 93
-
-
That's great! Moreover to save these attributes due to PostBack problem without creating ServerControl or something complicated: http://stackoverflow.com/a/33657968/2093328 – Andrei Shostik Nov 11 '15 at 19:08
-
1This is better than the accepted answer. Little note; make sure to not include this attribute in ddl's html AppendDataBoundItems="True" – Baz Guvenkaya Nov 08 '16 at 06:37
-
2this little improvement of mhu's excellent solution works if there are more than one select tags (in this example, .select2 is the class in common to all the select tags) var groups = {}; $("select option[data-category]").each(function () { var sGroup = $.trim($(this).attr("data-category")); groups[sGroup] = true; }); $.each(groups, function (c) { $(".select2").each(function () { $(this).find("option[data-category='" + c + "']").wrapAll(' – Fabio Napodano Dec 13 '17 at 12:42
This is old but since I used the accepted answer recently I wanted to share my experience with it. While it does provide the correct markup, it caused problems for me, specifically whenever I tried to submit a form with any dropdownlist I would get the dreaded "Invalid postback or callback argument" error. After googling like a maniac, I came across this article which then links to this blog post. The code I ended up using was this:
public class DropDownListAdapter : System.Web.UI.WebControls.Adapters.WebControlAdapter {
protected override void RenderContents(HtmlTextWriter writer) {
var dropDownList = (DropDownList)Control;
var items = dropDownList.Items;
var groups = (from p in items.OfType<ListItem>()
group p by p.Attributes["Group"] into g
select new { Label = g.Key, Items = g.ToList() });
foreach (var group in groups)
{
if (!String.IsNullOrEmpty(group.Label))
{
writer.WriteBeginTag("optgroup");
writer.WriteAttribute("label", group.Label);
writer.Write(">");
}
var count = group.Items.Count();
if (count > 0)
{
var flag = false;
for (var i = 0; i < count; i++)
{
var item = group.Items[i];
writer.WriteBeginTag("option");
if (item.Selected)
{
if (flag)
{
throw new HttpException("Multiple selected items not allowed");
}
flag = true;
writer.WriteAttribute("selected", "selected");
}
if (!item.Enabled)
{
writer.WriteAttribute("disabled", "true");
}
writer.WriteAttribute("value", item.Value, true);
if (Page != null)
{
Page.ClientScript.RegisterForEventValidation(dropDownList.UniqueID, item.Value);
}
writer.Write('>');
HttpUtility.HtmlEncode(item.Text, writer);
writer.WriteEndTag("option");
writer.WriteLine();
}
}
if (!String.IsNullOrEmpty(group.Label))
{
writer.WriteEndTag("optgroup");
}
}
}
}
The listitems used here are created in the design page rather than the code-behind page like so:
<asp:ListItem Value="apple" Text="Apple" Group="Fruit"></asp:ListItem>
<asp:ListItem Value="banana" Text="Banana" Group="Fruit"></asp:ListItem>
<asp:ListItem Value="asparagus" Text="Asparagus" Group="Vegetable"></asp:ListItem>
<asp:ListItem Value="eggs" Text="Eggs" Group="Dairy"></asp:ListItem>
This produced the same markup as the accepted answer here but this didn't give me the postback error. I hope this saves someone some grief.

- 894
- 2
- 13
- 18
-
I'm pretty new to ASP.NET and cannot figure out where to put the dropdownlist adapter code. If I put it into my code behind then "group" is not a valid attribute of listitem. Does the dropdownlist adapter have to be added as a separate item, if so what? – Brent Oliver Mar 10 '17 at 16:45
-
1Yikes! I'm so sorry that I didn't see this until now. My apologies, @brent. This probably won't help you now but I'll try to help. The DropDownListAdapter class can go anywhere you think it makes sense. In your class to contain the dropdown, put a using statement to where the class is kept. In Page_PreInit, I put this statement in `Context.Request.Browser.Adapters.Add(typeof(DropDownList).FullName, typeof(DropDownListAdapter).FullName);` . The warning you were getting will still be there, but it should still work. – Doug F May 31 '17 at 15:33
----- in .cs -----
List<SelectListItem> sl = new List<SelectListItem>();
sl.Add(new SelectListItem() { Text = "My text", Value = "1", Group = new SelectListGroup() { Name = "First Group" } });
sl.Add(new SelectListItem() { Text = "My text 2", Value = "2", Group = new SelectListGroup() { Name = "First Group" } });
var sl1 = new SelectList(sl,"Value","Text","Group.Name",-1);
ViewData["MyList"] = sl1;
----- in .cshtml -----
Html.DropDownList("My List", ViewData["MyList"] as SelectList,
"-- No Preference --",
new {
@class = "ui-widget ui-corner-all square_corners searchPageDropdown"
}))

- 33
- 1
- 5
1) Add the dropdownlist adapter class from here to your project
2) Add App_Browsers folder to your project(right click on project => Add => Add ASP.NET folder => App_Browsers)
3) Add a browser file into App_Browsers page and add the following code below inside browsers tag.
<browser refID="WebKit">
<controlAdapters>
<adapter controlType="System.Web.UI.WebControls.DropDownList"
adapterType="YourAdapterClasse'sNameSPace.DropDownListAdapter" />
</controlAdapters>
</browser>
refId = WebKit supports chrome and safari browsers
4) Then you can add items to your dropdownlist ListItem item1 = new ListItem("Smith Street", "1"); item1.Attributes["OptionGroup"] = "Darwin";
ListItem item2 = new ListItem("Mitchel Street", "1");
item2.Attributes["OptionGroup"] = "Darwin";
ListItem item3 = new ListItem("Hunter Street", "2");
item3.Attributes["OptionGroup"] = "Sydney";
ListItem item4 = new ListItem("BroadwaY", "4");
item4.Attributes["OptionGroup"] = "Sydney";
venuedropdown.Items.Add(item1);
venuedropdown.Items.Add(item2);
venuedropdown.Items.Add(item3);
venuedropdown.Items.Add(item4);

- 339
- 2
- 11
Here's what I did, with jquery-only, no server side changes:
/* Add Option Groups to Note Dropdown */
var $select = $('#<%= DropDownListIDHere.ClientID %>');
var optGroup;
$('#<%= DropDownListIDHere.ClientID %> option').each(function () {
if ($(this).val() == '<') {
/* Opener */
optGroup = $('<optGroup>').attr('label', $(this).text());
} else if ($(this).val() == '>') {
/* Closer */
$('</optGroup>').appendTo(optGroup);
optGroup.appendTo($select);
optGroup = null;
} else {
/* Normal Item */
if (optGroup) {
$('<option>' + $(this).text() + '</option>').attr('value', $(this).val()).appendTo(optGroup);
} else {
$('<option>' + $(this).text() + '</option>').attr('value', $(this).val()).appendTo($select);
}
}
$(this).remove();
});
Then, you just add specific items as openers and closers with value < and > like so:
<asp:ListItem Text="Group 1" Value="<" />
<asp:ListItem Text="Thing 1" Value="1111" />
<asp:ListItem Text="Thing 2" Value="2222" />
<asp:ListItem Text="Thing 3" Value="3333" />
<asp:ListItem Text="Group 1" Value=">" />
Super simple, no new controls needed, only targets the select you want to change, and doesn't require every item to be in an optgroup.

- 2,026
- 1
- 22
- 25
-
For my application, this solution was a great start but lacked two features: 1) persisting the groupings across postbacks inside an UpdatePanel and 2) persisting the selected option. – CAK2 Feb 22 '19 at 20:51
I really liked @ScottRFrost's answer above. But, it fell two bricks short of my load as of this writing:
- It did not persist the groupings across postbacks inside my UpdatePanel and
- It did not persist the drop down selected value across postbacks.
Here is my extensions to the client side of his answer:
$(document).ready(function () {
ddlOptionGrouping();
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(ddlOptionGrouping);
function ddlOptionGrouping() {
/* Add Option Groups to asp:DropdownList */
var $select = $('#<%= aspDropDownList.ClientID %>');
var optGroup;
$('#<%= aspDropDownList.ClientID %> option').each(function () {
if ($(this).val() == '<') {
/* Opener */
optGroup = $('<optGroup>').attr('label', $(this).text());
} else if ($(this).val() == '>') {
/* Closer */
$('</optGroup>').appendTo(optGroup);
optGroup.appendTo($select);
optGroup = null;
} else {
/* Normal Item */
let $option = $('<option>' + $(this).text() + '</option>').attr('value', $(this).val());
if (this.hasAttribute('selected')) {
$option.attr('selected', $(this).attr('selected'));
}
if (optGroup) {
$option.appendTo(optGroup);
} else {
$option.appendTo($select);
}
}
$(this).remove();
});
}
});

- 1,892
- 1
- 15
- 17
I use this method, which avoids ViewBag and ViewData:
View model:
public class PageViewModel
{
public int SelectedDropdownItem { get; set; }
public IEnumerable<SelectListItem> DropdownList { get; set; }
}
Example entity model (for this example):
public class Users
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsAdministrator { get; set; }
public bool IsDefault { get; set; }
}
Controller:
// Get list for the dropdown (this uses db values)
var userList = db.Users.ToList();
// Define the Groups
var group1 = new SelectListGroup { Name = "Administrators" };
var group2 = new SelectListGroup { Name = "Users" };
// Note - the -1 is needed at the end of this - pre-selected value is determined further down
// Note .OrderBy() determines the order in which the groups are displayed in the dropdown
var dropdownList = new SelectList(userList.Select(item => new SelectListItem
{
Text = item.Name,
Value = item.Id,
// Assign the Group to the item by some appropriate selection method
Group = item.IsAdministrator ? group1 : group2
}).OrderBy(a => a.Group.Name).ToList(), "Value", "Text", "Group.Name", -1);
// Assign values to ViewModel
var viewModel = new PageViewModel
{
// Assign the pre-selected dropdown value by appropriate selction method (if needed)
SelectedDropdownItem = userList.FirstOrDefault(a => a.IsDefault).Id,
DropdownList = dropdownList
};
View:
<!-- If needed, change 'null' to "Please select item" -->
@Html.DropDownListFor(a => a.SelectedDropdownItem, Model.DropdownList, null, new { @class = "some-class" })
Hope this helps someone to avoid what happened to me - way too much time spent finding a strongly-typed method.

- 3,318
- 10
- 38
- 39
this little improvement to the client side of mhu's excellent solution also works if there are more than one select tags.
With his version, indeed, each select tag gets filled with one copy of each option tag of each select tag
(in this example, .select2 is the class in common to all the select tags)
function filterUserGroups()
{
var groups = {};
$("select option[data-category]").each(function () {
var sGroup = $.trim($(this).attr("data-category"));
groups[sGroup] = true;
});
$.each(groups, function (c) {
$(".select2").each(function () {
$(this).find("option[data-category='" + c + "']").wrapAll('<optgroup label="' + c + '">');
})
});
}

- 1,210
- 1
- 16
- 17
I prefered a client script solution to avoid the complications at postback, so i solved it like this. Here I use an ordinary html select just to show it in function, but it works with ASP DropDownList too:
<script>
function addOptGrp() {
var t, p
for (var i = 0; i < arguments.length; i=i+2) {
t=arguments[i];
p=arguments[i+1]-i/2;
var scripts = document.getElementsByTagName('script');
/* On the next line scripts[scripts.length - 1] is the last script
in the page, which by definition will always be the calling script,
no matter how many such scripts you have */
var ddl = scripts[scripts.length - 1].previousElementSibling;
var og = document.createElement("optgroup");
og.label = t;
ddl.add(og,p);
ddl.selectedIndex = ddl.selectedIndex // needed for UI;
}
}
</script>
<select style="width:100px;">
<option>Apple</option>
<option>Pear</option>
<option>Banana</option>
<option>Orange</option>
</select>
<!-- here I just add the option groups in a script that immediately executes.
Notice that the script must follow immediately after the select (or DropDownList) -->
<script>addOptGrp(
'Simple fruits', 0,
'Very exotic fruits', 3
)</script>
<br><br>
<!-- Here comes another one -->
<select style="width:100px;">
<option>Red</option>
<option>Blue</option>
<option>Yellow</option>
<option>Green</option>
<option>Magenta</option>
<option>White</option>
</select>
<script>addOptGrp(
'Some colors', 0,
'Some more colors', 4
)</script>
This function fulfilled the needs I had today. Another day one might need to use a modified version, for example where it converts ordinary options to optgroups, depending on some prefix or so.

- 1,584
- 19
- 14