Controllo Dropdownlist con s per asp.net (webforms)?

Qualcuno può raccomandare un controllo dropdownlist per asp.net (3.5) che può eseguire il rendering di gruppi di opzioni? Grazie

Ho usato il controllo standard in passato e ho aggiunto un semplice ControlAdapter che avrebbe sovrascritto il comportamento predefinito in modo che potesse rendere i in determinati punti. Funziona alla grande anche se disponi di controlli che non richiedono il comportamento speciale, perché la funzione aggiuntiva non interferisce.

Nota che questo era per uno scopo specifico e scritto in .Net 2.0, quindi potrebbe non essere adatto a te, ma dovrebbe almeno darti un punto di partenza. Inoltre, devi collegarlo usando un file .browser nel tuo progetto (vedi la fine del post per un esempio).

'This codes makes the dropdownlist control recognize items with "--" 'for the label or items with an OptionGroup attribute and render them 'as  instead of  

Ecco un’implementazione C # della stessa class:

 /* This codes makes the dropdownlist control recognize items with "--" * for the label or items with an OptionGroup attribute and render them * as  instead of  

Il mio file del browser è stato chiamato “App_Browsers \ BrowserFile.browser” e assomigliava a questo:

         

Ho usato JQuery per realizzare questo compito. Per prima cosa ho aggiunto un nuovo attributo per ogni ListItem dal backend e poi ho usato wrapAll() metodo wrapAll() JQuery per creare gruppi …

C #:

 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:

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

Grazie Joel! tutti … ecco la versione C # se lo vuoi:

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

Il codice sopra rende il tag di chiusura per l’optgroup prima di qualsiasi opzione, quindi le opzioni non vengono indentate come dovrebbero, oltre al markup che non rappresenta correttamente il raggruppamento. Ecco la mia versione leggermente modificata del codice di 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); } } } 

Usalo in questo modo:

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

ed ecco il markup generato (rientrato per facilità di visualizzazione):

   

Il progetto Sharp Pieces su CodePlex risolve questo (e molti altri) limiti di controllo.

Io uso il riflettore per capire perché non è supportato. Ecco perché Nel metodo di rendering di ListControl non esiste nessuna condizione per creare l’optgroup.

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

Così ho creato il mio Dropdown Control con un override del metodo RenderContents. C’è il mio controllo. Funziona bene. Io uso esattamente lo stesso codice di Microsoft, basta aggiungere una piccola condizione per supportare listItem che ha optgroup per creare un optgroup e non un’opzione.

Dammi un po ‘di feedback

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

Sulla base dei post precedenti ho creato la versione ac # di questo controllo con lo stato della vista di lavoro.

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

Spero che questo aiuti alcune persone 🙂

Un approccio più generico alla soluzione basata su jQuery di Irfan :

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

cliente

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

L’ho fatto usando un ripetitore esterno per i gruppi select e opt e un ripetitore interno per gli elementi all’interno di quel gruppo:

     ">            

L’origine dati per outerRepeater è un semplice raggruppamento come segue:

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

E per ottenere il carattere di raggruppamento alfa:

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

Non è una soluzione perfetta ma funziona. La soluzione corretta sarebbe quella di non utilizzare più WebForms, ma noi 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; } 

Testato su Chrome 16, Firefox 9 e IE8.

Come funzionano le risposte sopra che sovraccaricano il metodo RenderContents. Devi anche ricordare di modificare lo stato di visualizzazione. Ho riscontrato un problema durante l’utilizzo dello stato di visualizzazione non modificato in UpdatePanel. Questo ha parti prese dal progetto Sharp Pieces .

  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