I have divided the solution into two different parts. First part explains achieving the desired output using JQuery's native support for wrapping elements into other elements with the help of wrapAll() method. It takes only 1 line of code and we can have the groups created in the HTML. However, the drawback of this approach is the groups will disappear on postback. But if the DropdownList is small and we are not loading it from the database, then this approach is very suitable as it will save a lot of time and effort.
Ok, enough of the riff-raff and get down to business now.
Part 01 (static binding of list items with no support for postback)
Step 01: Creating a DropdownList
ListItemCollection list = new ListItemCollection();
list.Add(new ListItem("1", "1"));
list.Add(new ListItem("2", "2"));
list.Add(new ListItem("3", "3"));
list.Add(new ListItem("4", "4"));
list.Add(new ListItem("5", "5"));
list.Add(new ListItem("6", "6"));
list.Add(new ListItem("7", "7"));
list.Add(new ListItem("8", "8"));
list.Add(new ListItem("9", "9"));
list.Add(new ListItem("10", "10"));
ddl.DataSource = list;
ddl.DataBind();
Step 02: Creating a new attribute
We have to create a new attribute for every DropdownList item. This new attribute will be used for grouping in the JQuery code.
protected void ddl_DataBound(object sender, EventArgs e)
{
foreach (ListItem item in ((DropDownList)sender).Items)
{
if (System.Int32.Parse(item.Value) < 5)
item.Attributes.Add("classification", "LessThanFive");
else
item.Attributes.Add("classification", "GreaterThanFive");
}
}
Step 03: The fun part - Creating groups using JQuery WrapAll() method
<script>
$(document).ready(function() {
//Create groups for dropdown list
$("select.listsmall option[@classification='LessThanFive']").wrapAll("<optgroup label='Less than five'>");
$("select.listsmall option[@classification='GreaterThanFive']").wrapAll("<optgroup label='Greater than five'>");
});
</script>
Part 02 (dynamic binding of list items with full support for postback)
This approach not only creates groups using JQuery 1.2.6 but also allows DropdownList to remember these groups across postbacks. We will have to create a custom server control using DropdownList class and override SaveViewState() and LoadViewState() methods that will help the page ViewState remember and restore groups respectively.
As mentioned earlier, in this approach we have to create a custom server control with a bit of a backend code and override some of it's important methods. However, if you are one of those lazy developers like me who don't like the idea of creating controls specially custom server controls that reside in a separate assembly for a very tiny job such as the one in hand, then you can create control that is part of the same assembly by adding a new class file and deriving it's class from System.Web.UI.WebControls.DropDownList.
namespace ControlLibrary
{
public class DropdownList : System.Web.UI.WebControls.DropDownList
{
protected override object SaveViewState()
{
// Create an object array with one element for the CheckBoxList's
// ViewState contents, and one element for each ListItem in skmCheckBoxList
object[] state = new object[this.Items.Count + 1];
object baseState = base.SaveViewState();
state[0] = baseState;
// Now, see if we even need to save the view state
bool itemHasAttributes = false;
for (int i = 0; i < this.Items.Count; i++)
{
if (this.Items[i].Attributes.Count > 0)
{
itemHasAttributes = true;
// Create an array of the item's Attribute's keys and values
object[] attribKV = new object[this.Items[i].Attributes.Count * 2];
int k = 0;
foreach (string key in this.Items[i].Attributes.Keys)
{
attribKV[k++] = key;
attribKV[k++] = this.Items[i].Attributes[key];
}
state[i + 1] = attribKV;
}
}
// return either baseState or state, depending on whether or not
// any ListItems had attributes
if (itemHasAttributes)
return state;
else
return baseState;
}
protected override void LoadViewState(object savedState)
{
if (savedState == null) return;
// see if savedState is an object or object array
if (savedState is object[])
{
// we have an array of items with attributes
object [] state = (object[]) savedState;
base.LoadViewState(state[0]); // load the base state
for (int i = 1; i < state.Length; i++)
{
if (state[i] != null)
{
// Load back in the attributes
object [] attribKV = (object[]) state[i];
for (int k = 0; k < attribKV.Length; k += 2)
this.Items[i-1].Attributes.Add(attribKV[k].ToString(),
attribKV[k+1].ToString());
}
}
}
else
// we have just the base state
base.LoadViewState(savedState);
}
}
}
If you search on the internet for a solution to create <optgroup> in ASP.NET DropdownList, you will find some of the articles talking about overriding RenderContents() method of DropdownList to create
6 comments:
Thanks Irfan. That helped a lot. For the latest jquery, others can use this example:
$("option[classification='lessthanfive']").wrapAll("<optgroup label='less than five' />");
@Anonymous: Great, Thanks.
No demo link?
I'm encoding the groups in option values, that way getting rid of the need to override the view state:
$('select option[value|="G1"]').wrapAll(...);
$('select option[value|="G2"]').wrapAll(...);
The options have values like 'G1-Option1', 'G1-Options2', 'G2-Options3', etc
@Sven Vranckx, I don't think it would be a good idea to modify options values specially they are being passed to a another page or being used on the client side for comparison. We will have to remember to remove the group information (G1, G2 etc.). Changing the option values might cause more harm than good, I believe.
Thanks.
Thanks
Post a Comment