使用<optgroup>为asp.net(webforms)的下拉列表控件?

任何人都可以推荐一个asp.net(3.5),可以呈现选项组的下拉列表控件? 谢谢

过去我使用过标准控件,只是为它添加了一个简单的ControlAdapter ,它将覆盖默认的行为,所以它可以在某些地方渲染<optgroup>。 即使你有不需要特殊行为的控件,这个效果也很好,因为附加function不会阻碍。

请注意,这是为了一个特定的目的,并使用.Net 2.0编写,所以它可能不适合你,但至less应该给你一个出发点。 另外,您必须在项目中使用.browserfile将其挂起(请参阅post末尾的示例)。

'This codes makes the dropdownlist control recognize items with "--" 'for the label or items with an OptionGroup attribute and render them 'as <optgroup> instead of <option>. Public Class DropDownListAdapter Inherits System.Web.UI.WebControls.Adapters.WebControlAdapter Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) Dim list As DropDownList = Me.Control Dim currentOptionGroup As String Dim renderedOptionGroups As New Generic.List(Of String) For Each item As ListItem In list.Items Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value) If item.Attributes("OptionGroup") IsNot Nothing Then 'The item is part of an option group currentOptionGroup = item.Attributes("OptionGroup") If Not renderedOptionGroups.Contains(currentOptionGroup) Then 'the header was not written- do that first 'TODO: make this stack-based, so the same option group can be used more than once in longer select element (check the most-recent stack item instead of anything in the list) If (renderedOptionGroups.Count > 0) Then RenderOptionGroupEndTag(writer) 'need to close previous group End If RenderOptionGroupBeginTag(currentOptionGroup, writer) renderedOptionGroups.Add(currentOptionGroup) End If RenderListItem(item, writer) ElseIf item.Text = "--" Then 'simple separator RenderOptionGroupBeginTag("--", writer) RenderOptionGroupEndTag(writer) Else 'default behavior: render the list item as normal RenderListItem(item, writer) End If Next item If renderedOptionGroups.Count > 0 Then RenderOptionGroupEndTag(writer) End If End Sub Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter) writer.WriteBeginTag("optgroup") writer.WriteAttribute("label", name) writer.Write(HtmlTextWriter.TagRightChar) writer.WriteLine() End Sub Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter) writer.WriteEndTag("optgroup") writer.WriteLine() End Sub Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter) writer.WriteBeginTag("option") writer.WriteAttribute("value", item.Value, True) If item.Selected Then writer.WriteAttribute("selected", "selected", False) End If For Each key As String In item.Attributes.Keys writer.WriteAttribute(key, item.Attributes(key)) Next key writer.Write(HtmlTextWriter.TagRightChar) HttpUtility.HtmlEncode(item.Text, writer) writer.WriteEndTag("option") writer.WriteLine() End Sub End Class 

这是一个相同的类的C#实现:

 /* This codes makes the dropdownlist control recognize items with "--" * for the label or items with an OptionGroup attribute and render them * as <optgroup> instead of <option>. */ public class DropDownListAdapter : WebControlAdapter { protected override void RenderContents(HtmlTextWriter writer) { //System.Web.HttpContext.Current.Response.Write("here"); var list = (DropDownList)this.Control; string currentOptionGroup; var renderedOptionGroups = new List<string>(); foreach (ListItem item in list.Items) { Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value); //Is the item part of an option group? if (item.Attributes["OptionGroup"] != null) { currentOptionGroup = item.Attributes["OptionGroup"]; //Was the option header already written, then just render the list item if (renderedOptionGroups.Contains(currentOptionGroup)) RenderListItem(item, writer); //The header was not written,do that first else { //Close previous group if (renderedOptionGroups.Count > 0) RenderOptionGroupEndTag(writer); RenderOptionGroupBeginTag(currentOptionGroup, writer); renderedOptionGroups.Add(currentOptionGroup); RenderListItem(item, writer); } } //Simple separator else if (item.Text == "--") { RenderOptionGroupBeginTag("--", writer); RenderOptionGroupEndTag(writer); } //Default behavior, render the list item as normal else 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(); } } 

我的浏览器文件被命名为“App_Browsers \ BrowserFile.browser”,看起来像这样:

 <!-- You can find existing browser definitions at <windir>\Microsoft.NET\Framework\<ver>\CONFIG\Browsers --> <browsers> <browser refID="Default"> <controlAdapters> <adapter controlType="System.Web.UI.WebControls.DropDownList" adapterType="DropDownListAdapter" /> </controlAdapters> </browser> </browsers> 

我已经使用JQuery来实现这个任务。 我首先从后端为每个ListItem添加了一个新属性,然后在JQuery wrapAll()方法中使用该属性来创build组

后端代码

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"); }
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"); } 

JQuery部分:

<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>
<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> 

谢谢Joel! 大家…这是C#版本,如果你想要它:

 using System; using System.Web.UI.WebControls.Adapters; using System.Web.UI; using System.Web.UI.WebControls; using System.Collections.Generic; using System.Web; //This codes makes the dropdownlist control recognize items with "--"' //for the label or items with an OptionGroup attribute and render them' //as instead of .' public class DropDownListAdapter : WebControlAdapter { protected override void RenderContents(HtmlTextWriter writer) { DropDownList list = Control as DropDownList; string currentOptionGroup; List renderedOptionGroups = new List(); foreach(ListItem item in list.Items) { if (item.Attributes["OptionGroup"] != null) { //'The item is part of an option group' currentOptionGroup = item.Attributes["OptionGroup"]; //'the option header was already written, just render the list item' if(renderedOptionGroups.Contains(currentOptionGroup)) RenderListItem(item, writer); else { //the header was not written- do that first' if (renderedOptionGroups.Count > 0) RenderOptionGroupEndTag(writer); //'need to close previous group' RenderOptionGroupBeginTag(currentOptionGroup, writer); renderedOptionGroups.Add(currentOptionGroup); RenderListItem(item, writer); } } else if (item.Text == "--") //simple separator { RenderOptionGroupBeginTag("--", writer); RenderOptionGroupEndTag(writer); } else { //default behavior: render the list item as normal' 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(); } } 

上面的代码在任何选项之前呈现optgroup的结束标记,因此除了不正确地表示分组的标记之外,这些选项不会被缩进。 这里是我的稍微修改的Tom的代码版本:

  public class ExtendedDropDownList : System.Web.UI.WebControls.DropDownList { public const string OptionGroupTag = "optgroup"; private const string OptionTag = "option"; protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { ListItemCollection items = this.Items; int count = items.Count; string tag; string optgroupLabel; if (count > 0) { bool flag = false; string prevOptGroup = null; for (int i = 0; i < count; i++) { tag = OptionTag; optgroupLabel = null; ListItem item = items[i]; if (item.Enabled) { if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null) { optgroupLabel = item.Attributes[OptionGroupTag]; if (prevOptGroup != optgroupLabel) { if (prevOptGroup != null) { writer.WriteEndTag(OptionGroupTag); } writer.WriteBeginTag(OptionGroupTag); if (!string.IsNullOrEmpty(optgroupLabel)) { writer.WriteAttribute("label", optgroupLabel); } writer.Write('>'); } item.Attributes.Remove(OptionGroupTag); prevOptGroup = optgroupLabel; } else { if (prevOptGroup != null) { writer.WriteEndTag(OptionGroupTag); } prevOptGroup = null; } writer.WriteBeginTag(tag); if (item.Selected) { if (flag) { this.VerifyMultiSelect(); } flag = true; writer.WriteAttribute("selected", "selected"); } writer.WriteAttribute("value", item.Value, true); if (item.Attributes != null && item.Attributes.Count > 0) { item.Attributes.Render(writer); } if (optgroupLabel != null) { item.Attributes.Add(OptionGroupTag, optgroupLabel); } if (this.Page != null) { this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value); } writer.Write('>'); HttpUtility.HtmlEncode(item.Text, writer); writer.WriteEndTag(tag); writer.WriteLine(); if (i == count - 1) { if (prevOptGroup != null) { writer.WriteEndTag(OptionGroupTag); } } } } } } protected override object SaveViewState() { object[] state = new object[this.Items.Count + 1]; object baseState = base.SaveViewState(); state[0] = baseState; bool itemHasAttributes = false; for (int i = 0; i < this.Items.Count; i++) { if (this.Items[i].Attributes.Count > 0) { itemHasAttributes = true; object[] attributes = new object[this.Items[i].Attributes.Count * 2]; int k = 0; foreach (string key in this.Items[i].Attributes.Keys) { attributes[k] = key; k++; attributes[k] = this.Items[i].Attributes[key]; k++; } state[i + 1] = attributes; } } if (itemHasAttributes) return state; return baseState; } protected override void LoadViewState(object savedState) { if (savedState == null) return; if (!(savedState.GetType().GetElementType() == null) && (savedState.GetType().GetElementType().Equals(typeof(object)))) { object[] state = (object[])savedState; base.LoadViewState(state[0]); for (int i = 1; i < state.Length; i++) { if (state[i] != null) { object[] attributes = (object[])state[i]; for (int k = 0; k < attributes.Length; k += 2) { this.Items[i - 1].Attributes.Add (attributes[k].ToString(), attributes[k + 1].ToString()); } } } } else { base.LoadViewState(savedState); } } } 

像这样使用它:

  ListItem item1 = new ListItem("option1"); item1.Attributes.Add("optgroup", "CatA"); ListItem item2 = new ListItem("option2"); item2.Attributes.Add("optgroup", "CatA"); ListItem item3 = new ListItem("option3"); item3.Attributes.Add("optgroup", "CatB"); ListItem item4 = new ListItem("option4"); item4.Attributes.Add("optgroup", "CatB"); ListItem item5 = new ListItem("NoOptGroup"); ddlTest.Items.Add(item1); ddlTest.Items.Add(item2); ddlTest.Items.Add(item3); ddlTest.Items.Add(item4); ddlTest.Items.Add(item5); 

这里是生成的标记(为便于查看而缩进):

  <select name="ddlTest" id="Select1"> <optgroup label="CatA"> <option selected="selected" value="option1">option1</option> <option value="option2">option2</option> </optgroup> <optgroup label="CatB"> <option value="option3">option3</option> <option value="option4">option4</option> </optgroup> <option value="NoOptGroup">NoOptGroup</option> </select> 

CodePlex上的Sharp Pieces项目解决了这个(和其他几个)控制限制。

我使用reflection器来查看为什么不支持。 这是为什么。 在ListControl的渲染方法中,不存在创buildoptgroup的条件。

 protected internal override void RenderContents(HtmlTextWriter writer) { ListItemCollection items = this.Items; int count = items.Count; if (count > 0) { bool flag = false; for (int i = 0; i < count; i++) { ListItem item = items[i]; if (item.Enabled) { writer.WriteBeginTag("option"); if (item.Selected) { if (flag) { this.VerifyMultiSelect(); } flag = true; writer.WriteAttribute("selected", "selected"); } writer.WriteAttribute("value", item.Value, true); if (item.HasAttributes) { item.Attributes.Render(writer); } if (this.Page != null) { this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value); } writer.Write('>'); HttpUtility.HtmlEncode(item.Text, writer); writer.WriteEndTag("option"); writer.WriteLine(); } } } } 

所以我创build了自己的下拉控件,用方法RenderContents覆盖。 有我的控制。 工作正常。 我使用完全相同的Microsoft代码,只是添加一点条件来支持具有属性optgroup的listItem来创build一个optgroup而不是一个选项。

给我一些反馈

 public class DropDownListWithOptionGroup : DropDownList { public const string OptionGroupTag = "optgroup"; private const string OptionTag = "option"; protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { ListItemCollection items = this.Items; int count = items.Count; string tag; string optgroupLabel; if (count > 0) { bool flag = false; for (int i = 0; i < count; i++) { tag = OptionTag; optgroupLabel = null; ListItem item = items[i]; if (item.Enabled) { if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null) { tag = OptionGroupTag; optgroupLabel = item.Attributes[OptionGroupTag]; } writer.WriteBeginTag(tag); // NOTE(cboivin): Is optionGroup if (!string.IsNullOrEmpty(optgroupLabel)) { writer.WriteAttribute("label", optgroupLabel); } else { if (item.Selected) { if (flag) { this.VerifyMultiSelect(); } flag = true; writer.WriteAttribute("selected", "selected"); } writer.WriteAttribute("value", item.Value, true); if (item.Attributes != null && item.Attributes.Count > 0) { item.Attributes.Render(writer); } if (this.Page != null) { this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value); } } writer.Write('>'); HttpUtility.HtmlEncode(item.Text, writer); writer.WriteEndTag(tag); writer.WriteLine(); } } } } } 

基于上面的post,我创build了这个控件的工作视图状态的AC#版本。

  public const string OptionGroupTag = "optgroup"; private const string OptionTag = "option"; protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { ListItemCollection items = this.Items; int count = items.Count; string tag; string optgroupLabel; if (count > 0) { bool flag = false; for (int i = 0; i < count; i++) { tag = OptionTag; optgroupLabel = null; ListItem item = items[i]; if (item.Enabled) { if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null) { tag = OptionGroupTag; optgroupLabel = item.Attributes[OptionGroupTag]; } writer.WriteBeginTag(tag); // NOTE(cboivin): Is optionGroup if (!string.IsNullOrEmpty(optgroupLabel)) { writer.WriteAttribute("label", optgroupLabel); } else { if (item.Selected) { if (flag) { this.VerifyMultiSelect(); } flag = true; writer.WriteAttribute("selected", "selected"); } writer.WriteAttribute("value", item.Value, true); if (item.Attributes != null && item.Attributes.Count > 0) { item.Attributes.Render(writer); } if (this.Page != null) { this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value); } } writer.Write('>'); HttpUtility.HtmlEncode(item.Text, writer); writer.WriteEndTag(tag); writer.WriteLine(); } } } } protected override object SaveViewState() { object[] state = new object[this.Items.Count + 1]; object baseState = base.SaveViewState(); state[0] = baseState; bool itemHasAttributes = false; for (int i = 0; i < this.Items.Count; i++) { if (this.Items[i].Attributes.Count > 0) { itemHasAttributes = true; object[] attributes = new object[this.Items[i].Attributes.Count * 2]; int k = 0; foreach (string key in this.Items[i].Attributes.Keys) { attributes[k] = key; k++; attributes[k] = this.Items[i].Attributes[key]; k++; } state[i + 1] = attributes; } } if (itemHasAttributes) return state; return baseState; } protected override void LoadViewState(object savedState) { if (savedState == null) return; if (!(savedState.GetType().GetElementType() == null) && (savedState.GetType().GetElementType().Equals(typeof(object)))) { object[] state = (object[])savedState; base.LoadViewState(state[0]); for (int i = 1; i < state.Length; i++) { if (state[i] != null) { object[] attributes = (object[])state[i]; for (int k = 0; k < attributes.Length; k += 2) { this.Items[i - 1].Attributes.Add (attributes[k].ToString(), attributes[k + 1].ToString()); } } } } else { base.LoadViewState(savedState); } } 

我希望这有助于一些人:-)

Irfan的基于jQuery的解决scheme更通用的方法:

后端

 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"); ... 

客户

 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 + '">'); }); 

我已经做了这个使用外部中继器的select和optgroups和一个内部中继器为该组内的项目:

 <asp:Repeater ID="outerRepeater" runat="server"> <HeaderTemplate> <select id="<%= outerRepeater.ClientID %>"> </HeaderTemplate> <ItemTemplate> <optgroup label="<%# Eval("GroupText") %>"> <asp:Repeater runat="server" DataSource='<%# Eval("Items") %>'> <ItemTemplate> <option value="<%# Eval("Value") %>"><%# Eval("Text") %></option> </ItemTemplate> </asp:Repeater> </optgroup> </ItemTemplate> <FooterTemplate> </select> </FooterTemplate> </asp:Repeater> 

outerRepeater的数据源是一个简单的分组,如下所示:

 var data = (from o in thingsToDisplay group oby GetAlphaGrouping(o.Name) into g orderby g.Key select new { Alpha = g.Key, Items = g }); 

并获得alpha分组字符:

 private string GetAlphaGrouping(string value) { string firstChar = value.Substring(0, 1).ToUpper(); int unused; if (int.TryParse(firstChar, out unused)) return "#"; return firstChar.ToUpper(); } 

这不是一个完美的解决scheme,但它的工作。 正确的解决scheme将不再使用WebForms,而是我们MVC。 🙂

  // How to use: // 1. Create items in a select element or asp:DropDownList control // 2. Set value of an option or ListItem to "_group_", those will be converted to optgroups // 3. On page onload call createOptGroups(domElement), for example like this: // - var lst = document.getElementById('lst'); // - createOptGroups(lst, "_group_"); // 4. You can change groupMarkerValue to anything, I used "_group_" function createOptGroups(lst, groupMarkerValue) { // Get an array containing the options var childNodes = []; for (var i = 0; i < lst.options.length; i++) childNodes.push(lst.options[i]); // Get the selected element so we can preserve selection var selectedIndex = lst.selectedIndex; var selectedChild = childNodes[selectedIndex]; var selectedValue = selectedChild.value; // Remove all elements while (lst.hasChildNodes()) lst.removeChild(lst.childNodes[0]); // Go through the array of options and convert some into groups var group = null; for (var i = 0; i < childNodes.length; i++) { var node = childNodes[i]; if (node.value == groupMarkerValue) { group = document.createElement("optgroup"); group.label = node.text; group.value = groupMarkerValue; lst.appendChild(group); continue; } // Add to group or directly under list (group == null ? lst : group).appendChild(node); } // Preserve selected, no support for multi-selection here, sorry selectedChild.selected = true; } 

testing了Chrome 16,Firefox 9和IE8。

作为超负荷的RenderContents方法的答案确实工作。 你还必须记住改变视图状态。 在UpdatePanel中使用未经修改的视图状态时,我遇到了一个问题。 这个部分取自Sharp Pieces Project 。

  Protected Overloads Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) Dim list As DropDownList = Me Dim currentOptionGroup As String Dim renderedOptionGroups As New List(Of String)() For Each item As ListItem In list.Items If item.Attributes("OptionGroup") Is Nothing Then RenderListItem(item, writer) Else currentOptionGroup = item.Attributes("OptionGroup") If renderedOptionGroups.Contains(currentOptionGroup) Then RenderListItem(item, writer) Else If renderedOptionGroups.Count > 0 Then RenderOptionGroupEndTag(writer) End If RenderOptionGroupBeginTag(currentOptionGroup, writer) renderedOptionGroups.Add(currentOptionGroup) RenderListItem(item, writer) End If End If Next If renderedOptionGroups.Count > 0 Then RenderOptionGroupEndTag(writer) End If End Sub Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter) writer.WriteBeginTag("optgroup") writer.WriteAttribute("label", name) writer.Write(HtmlTextWriter.TagRightChar) writer.WriteLine() End Sub Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter) writer.WriteEndTag("optgroup") writer.WriteLine() End Sub Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter) writer.WriteBeginTag("option") writer.WriteAttribute("value", item.Value, True) If item.Selected Then writer.WriteAttribute("selected", "selected", False) End If For Each key As String In item.Attributes.Keys writer.WriteAttribute(key, item.Attributes(key)) Next writer.Write(HtmlTextWriter.TagRightChar) HttpUtility.HtmlEncode(item.Text, writer) writer.WriteEndTag("option") writer.WriteLine() End Sub Protected Overrides Function SaveViewState() As Object ' Create an object array with one element for the CheckBoxList's ' ViewState contents, and one element for each ListItem in skmCheckBoxList Dim state(Me.Items.Count + 1 - 1) As Object 'stupid vb array Dim baseState As Object = MyBase.SaveViewState() state(0) = baseState ' Now, see if we even need to save the view state Dim itemHasAttributes As Boolean = False For i As Integer = 0 To Me.Items.Count - 1 If Me.Items(i).Attributes.Count > 0 Then itemHasAttributes = True ' Create an array of the item's Attribute's keys and values Dim attribKV(Me.Items(i).Attributes.Count * 2 - 1) As Object 'stupid vb array Dim k As Integer = 0 For Each key As String In Me.Items(i).Attributes.Keys attribKV(k) = key k += 1 attribKV(k) = Me.Items(i).Attributes(key) k += 1 Next state(i + 1) = attribKV End If Next ' return either baseState or state, depending on whether or not ' any ListItems had attributes If (itemHasAttributes) Then Return state Else Return baseState End If End Function Protected Overrides Sub LoadViewState(ByVal savedState As Object) If savedState Is Nothing Then Return ' see if savedState is an object or object array If Not savedState.GetType.GetElementType() Is Nothing AndAlso savedState.GetType.GetElementType().Equals(GetType(Object)) Then ' we have just the base state MyBase.LoadViewState(savedState(0)) 'we have an array of items with attributes Dim state() As Object = savedState MyBase.LoadViewState(state(0)) '/ load the base state For i As Integer = 1 To state.Length - 1 If Not state(i) Is Nothing Then ' Load back in the attributes Dim attribKV() As Object = state(i) For k As Integer = 0 To attribKV.Length - 1 Step +2 Me.Items(i - 1).Attributes.Add(attribKV(k).ToString(), attribKV(k + 1).ToString()) Next End If Next Else 'load it normal MyBase.LoadViewState(savedState) End If End Sub