March 11, 2010 by Christoff Truter C# ASP.NET
The way ASP.net renders webcontrols don't always produce the desired markup, we might
require different markup for different browsers - e.g. a visitor browsing your website
from their mobile / cellphone.
We might even feel the need to change the way ASP.net renders webcontrols all together - luckily
microsoft provided a mechanism called WebControlAdapters.
For the purpose of this post, imagine the following scenario:
Your employer hands you two lists of options, which he wants selectable from DropDownLists, you
think to yourself, awesome I will throw it all together in one DropDownList and distinguish the lists
using optgroups - but suddenly it hits you that the standard DropDownList doesn't support optgroups.
After doing some quick research you decide to create a WebControlAdapter as workaround, even though it probably
isn't the greatest solution for this issue.
Step 1:
Within the App_Browsers, create a browser file, add the following xml:
<browsers> <browser refID="Default"> <controlAdapters> <adapter controlType="System.Web.UI.WebControls.DropDownList" adapterType="CSTruter.DropDownListAdapter" /> </controlAdapters> </browser> </browsers>
<asp:DropDownList runat="server" ID="ddlA"> <asp:ListItem Text="C#" Value="1" Group="Microsoft" ></asp:ListItem> <asp:ListItem Text="VB.net" Value="2" Group="Microsoft"></asp:ListItem> <asp:ListItem Text="PHP" Value="3" Group="Open Source"></asp:ListItem> <asp:ListItem Text="Java" Value="4" Group="Open Source" Enabled="false"></asp:ListItem> <asp:ListItem Text="Perl" Value="5" Group="Open Source"></asp:ListItem> </asp:DropDownList>
protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { // The current control being "adaptered" is available within context from the Control property DropDownList dropDownList = (DropDownList)Control; ListItemCollection items = dropDownList.Items; // Retrieve Optgrouping using LinQ var groups = (from p in items.OfType<ListItem>() group p by p.Attributes["Group"] into g select new { Label = g.Key, Items = g.ToList<ListItem>() }); foreach (var group in groups) { if (!String.IsNullOrEmpty(group.Label)) { writer.WriteBeginTag("optgroup"); writer.WriteAttribute("label", group.Label); writer.Write(">"); } int count = group.Items.Count(); if (count > 0) { bool flag = false; for (int i = 0; i < count; i++) { ListItem 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 (this.Page != null) { this.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"); } } }
private Object _ViewState; protected override void OnLoad(EventArgs e) { if (Page.IsPostBack) { if (_ViewState != null) { Object[] groups = (Object[])_ViewState; DropDownList dropDownList = (DropDownList)Control; // Add saved optgroups to ListItems for (Int32 i = 0; i < groups.Length; i++) { if (groups[i] != null) { dropDownList.Items[i].Attributes["Group"] = groups[i].ToString(); } } } } base.OnLoad(e); } protected override void LoadAdapterViewState(object state) { // Retrieve existing state _ViewState = state; } protected override object SaveAdapterViewState() { DropDownList dropDownList = (DropDownList)Control; Int32 count = dropDownList.Items.Count; Object[] values = new Object[count]; // Retrieve Optgrouping from ListItem for (int i = 0; i < count; i++) { values[i] = dropDownList.Items[i].Attributes["Group"]; } return values; }
February 17, 2017
ASP.NET WebControl Critical Analysis: DropDownListJune 27, 2011
Small addition to make April 26, 2022 by Melyssa
I posted a comment yesterday, so many years after this original post... It turns out as I started to use this little adapter, I realized that my listitem attributes were not being added to the options anymore (I use those to add specific data attributes to my options). If you find yourself in that situation, adding the following lines somewhere between the start and close tag for "option" will fix you right up: foreach(string attr in item.Attributes.Keys){ writer.WriteAttribute(attr, item.Attributes[attr]); }