ASP.net (C#) - WebControlAdapter
Source Code
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>
Step 2:
Define Group as an attribute within your ListItem(s), something you can also do in the
codebehind file, e.g. during databinding.
<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>
Step 3:
Create a class inheriting from class WebControlAdapter.
Override the RenderContents method (RenderBeginTag & RenderEndTag may also be overriden if needed)
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");
}
}
}
Consider:
When binding from a datasource, (which you wish to persist using ViewState) you will need to override
the adapter ViewState methods in order to persist optgroups, observe:
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;
}
If all goes according to plan, the WebControlAdapter will override the default markup for all DropDownLists in your solution.
Posted by - Christoff Truter
Date - 2010-03-11 21:47:12
Comments
Post comment