Ein Mitarbeiter hat mir folgendes gezeigt:
Er hat eine DropDownList und eine Schaltfläche auf einer Webseite. Hier ist der Code dahinter:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ListItem item = new ListItem("1");
item.Attributes.Add("title", "A");
ListItem item2 = new ListItem("2");
item2.Attributes.Add("title", "B");
DropDownList1.Items.AddRange(new[] {item, item2});
string s = DropDownList1.Items[0].Attributes["title"];
}
}
protected void Button1_Click(object sender, EventArgs e)
{
DropDownList1.Visible = !DropDownList1.Visible;
}
Beim Laden der Seite werden die QuickInfos der Elemente angezeigt, beim ersten Postback gehen die Attribute jedoch verloren. Warum ist dies der Fall und gibt es Problemumgehungen?
Antworten:
Ich hatte das gleiche Problem und wollte diese Ressource beitragen , bei der der Autor einen geerbten ListItem-Konsumenten erstellt hat, um Attribute für ViewState beizubehalten. Hoffentlich spart es jemandem die Zeit, die ich verschwendet habe, bis ich darauf gestoßen bin.
protected override object SaveViewState() { // create object array for Item count + 1 object[] allStates = new object[this.Items.Count + 1]; // the +1 is to hold the base info object baseState = base.SaveViewState(); allStates[0] = baseState; Int32 i = 1; // now loop through and save each Style attribute for the List foreach (ListItem li in this.Items) { Int32 j = 0; string[][] attributes = new string[li.Attributes.Count][]; foreach (string attribute in li.Attributes.Keys) { attributes[j++] = new string[] {attribute, li.Attributes[attribute]}; } allStates[i++] = attributes; } return allStates; } protected override void LoadViewState(object savedState) { if (savedState != null) { object[] myState = (object[])savedState; // restore base first if (myState[0] != null) base.LoadViewState(myState[0]); Int32 i = 1; foreach (ListItem li in this.Items) { // loop through and restore each style attribute foreach (string[] attribute in (string[][])myState[i++]) { li.Attributes[attribute[0]] = attribute[1]; } } } }
quelle
Danke, Laramie. Genau das, wonach ich gesucht habe. Es behält die Attribute perfekt.
Im Folgenden finden Sie eine Klassendatei, die ich mit Laramies Code erstellt habe, um eine Dropdown-Liste in VS2008 zu erstellen. Erstellen Sie die Klasse im Ordner App_Code. Verwenden Sie nach dem Erstellen der Klasse diese Zeile auf der Aspx-Seite, um sie zu registrieren:
<%@ Register TagPrefix="aspNewControls" Namespace="NewControls"%>
Damit können Sie das Steuerelement auf Ihr Webformular setzen
<aspNewControls:NewDropDownList ID="ddlWhatever" runat="server"> </aspNewControls:NewDropDownList>
Ok, hier ist die Klasse ...
using System; using System.Collections.Generic; using System.ComponentModel; using System.Security.Permissions; using System.Linq; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace NewControls { [DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")] public class NewDropDownList : DropDownList { [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] protected override object SaveViewState() { // create object array for Item count + 1 object[] allStates = new object[this.Items.Count + 1]; // the +1 is to hold the base info object baseState = base.SaveViewState(); allStates[0] = baseState; Int32 i = 1; // now loop through and save each Style attribute for the List foreach (ListItem li in this.Items) { Int32 j = 0; string[][] attributes = new string[li.Attributes.Count][]; foreach (string attribute in li.Attributes.Keys) { attributes[j++] = new string[] { attribute, li.Attributes[attribute] }; } allStates[i++] = attributes; } return allStates; } protected override void LoadViewState(object savedState) { if (savedState != null) { object[] myState = (object[])savedState; // restore base first if (myState[0] != null) base.LoadViewState(myState[0]); Int32 i = 1; foreach (ListItem li in this.Items) { // loop through and restore each style attribute foreach (string[] attribute in (string[][])myState[i++]) { li.Attributes[attribute[0]] = attribute[1]; } } } } } }
quelle
ddlWhatever.Items
, wird eine Null-Ausnahme von derddlWhatever
Idee ausgelöst, warum?UserControl
und versuchen, das zu erbenDropDownList
.Eine einfache Lösung besteht darin, die Tooltip-Attribute im
pre-render
Falle des Dropdowns hinzuzufügen. Änderungen am Status sollten bei derpre-render
Veranstaltung vorgenommen werden.Beispielcode :
protected void drpBrand_PreRender(object sender, EventArgs e) { foreach (ListItem _listItem in drpBrand.Items) { _listItem.Attributes.Add("title", _listItem.Text); } drpBrand.Attributes.Add("onmouseover", "this.title=this.options[this.selectedIndex].title"); }
quelle
Wenn Sie die Listenelemente nur beim ersten Laden der Seite laden möchten, müssen Sie ViewState aktivieren, damit das Steuerelement seinen Status dort serialisieren und neu laden kann, wenn die Seite zurückgesendet wird.
Es gibt mehrere Stellen, an denen ViewState aktiviert werden kann. Überprüfen Sie den
<pages/>
Knoten in der Datei web.config und in der<%@ page %>
Direktive oben in der Aspx-Datei selbst auf dieEnableViewState
Eigenschaft. Diese Einstellung muss vorhanden sein, damittrue
ViewState funktioniert.Wenn Sie ViewState nicht verwenden möchten, entfernen Sie einfach
if (!IsPostBack) { ... }
den Code aus dem Code, der das hinzufügt,ListItems
und die Elemente werden bei jedem Postback neu erstellt.Edit: Ich entschuldige mich - ich habe Ihre Frage falsch verstanden. Sie haben Recht, dass die Attribute kein Postback überleben, da sie in ViewState nicht serialisiert sind. Sie müssen diese Attribute bei jedem Postback erneut hinzufügen.
quelle
Eine einfache Lösung: Rufen Sie Ihre Dropdown-Ladefunktion für das Klickereignis auf, bei dem Sie eine Rückmeldung anfordern.
quelle
Typische Lösungen für dieses Problem sind das Erstellen neuer Steuerelemente, die unter normalen Umständen nicht ganz realisierbar sind. Es gibt eine einfache, aber triviale Lösung für dieses Problem.
Das Problem ist, dass der
ListItem
seine Attribute beim Postback verliert. Die Liste selbst verliert jedoch niemals benutzerdefinierte Attribute. Man kann dies also auf einfache und doch effektive Weise nutzen.Schritte:
Serialisieren Sie Ihre Attribute mit dem Code in der obigen Antwort ( https://stackoverflow.com/a/3099755/3624833 ).
Speichern Sie es in einem benutzerdefinierten Attribut des ListControl (Dropdown-Liste, Checkliste, was auch immer).
Lesen Sie beim Zurücksenden das benutzerdefinierte Attribut aus dem ListControl zurück und deserialisieren Sie es dann wieder als Attribute.
Hier ist der Code, mit dem ich Attribute (de) serialisiert habe (Was ich tun musste, war zu verfolgen, welche Elemente der Liste ursprünglich als ausgewählt gerendert wurden, als sie aus dem Backend abgerufen wurden, und dann Zeilen gemäß den von vorgenommenen Änderungen zu speichern oder zu löschen der Benutzer auf der Benutzeroberfläche):
string[] selections = new string[Users.Items.Count]; for(int i = 0; i < Users.Items.Count; i++) { selections[i] = string.Format("{0};{1}", Users.Items[i].Value, Users.Items[i].Selected); } Users.Attributes["data-item-previous-states"] = string.Join("|", selections);
(oben ist "Benutzer" ein
CheckboxList
Steuerelement).Beim Zurücksenden (in meinem Fall ein Ereignis zum Senden der Schaltfläche "Klicken") verwende ich den folgenden Code, um dasselbe abzurufen und zur Nachbearbeitung in einem Wörterbuch zu speichern:
Dictionary<Guid, bool> previousStates = new Dictionary<Guid, bool>(); string[] state = Users.Attributes["data-item-previous-states"].Split(new char[] {'|'}, StringSplitOptions.RemoveEmptyEntries); foreach(string obj in state) { string[] kv = obj.Split(new char[] { ';' }, StringSplitOptions.None); previousStates.Add(kv[0], kv[1]); }
(PS: Ich habe eine Bibliotheksfunktion, die Fehlerbehandlung und Datenkonvertierung durchführt und diese hier der Kürze halber weglässt.)
quelle
Hier ist der VB.Net-Code der von Laramie vorgeschlagenen und von gleapman verfeinerten Lösung.
Update: Der Code, den ich unten gepostet habe, ist eigentlich für das ListBox-Steuerelement. Ändern Sie einfach die Vererbung in DropDownList und benennen Sie die Klasse um.
Imports System.Collections.Generic Imports System.ComponentModel Imports System.Security.Permissions Imports System.Linq Imports System.Text Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace CustomControls <DefaultProperty("Text")> _ <ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")> Public Class PersistentListBox Inherits ListBox <Bindable(True)> _ <Category("Appearance")> _ <DefaultValue("")> _ <Localizable(True)> _ Protected Overrides Function SaveViewState() As Object ' Create object array for Item count + 1 Dim allStates As Object() = New Object(Me.Items.Count + 1) {} ' The +1 is to hold the base info Dim baseState As Object = MyBase.SaveViewState() allStates(0) = baseState Dim i As Int32 = 1 ' Now loop through and save each attribute for the List For Each li As ListItem In Me.Items Dim j As Int32 = 0 Dim attributes As String()() = New String(li.Attributes.Count - 1)() {} For Each attribute As String In li.Attributes.Keys attributes(j) = New String() {attribute, li.Attributes(attribute)} j += 1 Next allStates(i) = attributes i += 1 Next Return allStates End Function Protected Overrides Sub LoadViewState(savedState As Object) If savedState IsNot Nothing Then Dim myState As Object() = DirectCast(savedState, Object()) ' Restore base first If myState(0) IsNot Nothing Then MyBase.LoadViewState(myState(0)) End If Dim i As Int32 = 0 For Each li As ListItem In Me.Items ' Loop through and restore each attribute ' NOTE: Ignore the first item as that is the base state and is represented by a Triplet struct i += 1 For Each attribute As String() In DirectCast(myState(i), String()()) li.Attributes(attribute(0)) = attribute(1) Next Next End If End Sub End Class End Namespace
quelle
@Sujay Sie können dem Wertattribut der Dropdown-Liste (wie im CSV-Stil) einen durch Semikolon getrennten Text hinzufügen und String.Split (';') verwenden, um 2 "Werte" aus dem einen Wert zu erhalten, um dies zu umgehen ohne dass eine neue Benutzersteuerung erstellt werden muss. Vor allem, wenn Sie nur wenige zusätzliche Attribute haben und diese nicht zu lang sind. Sie können auch einen JSON-Wert in das Wertattribut der Dropdown-Liste verwenden und dann von dort aus alles analysieren, was Sie benötigen.
quelle
//In the same block where the ddl is loaded (assuming the dataview is retrieved whether postback or not), search for the listitem and re-apply the attribute if(IsPostBack) foreach (DataRow dr in dvFacility.Table.Rows) { //search the listitem ListItem li = ddl_FacilityFilter.Items.FindByValue(dr["FACILITY_CD"].ToString()); if (li!=null) { li.Attributes.Add("Title", dr["Facility_Description"].ToString()); } } //end for each
quelle
Einfache Lösung ohne ViewState, die neue Serversteuerung oder einen neuen Komplex erstellt:
Erstellen:
public void AddItemList(DropDownList list, string text, string value, string group = null, string type = null) { var item = new ListItem(text, value); if (!string.IsNullOrEmpty(group)) { if (string.IsNullOrEmpty(type)) type = "group"; item.Attributes["data-" + type] = group; } list.Items.Add(item); }
Aktualisierung:
public void ChangeItemList(DropDownList list, string eq, string group = null, string type = null) { var listItem = list.Items.Cast<ListItem>().First(item => item.Value == eq); if (!string.IsNullOrEmpty(group)) { if (string.IsNullOrEmpty(type)) type = "group"; listItem.Attributes["data-" + type] = group; } }
Beispiel:
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { using (var context = new WOContext()) { context.Report_Types.ToList().ForEach(types => AddItemList(DropDownList1, types.Name, types.ID.ToString(), types.ReportBaseTypes.Name)); DropDownList1.DataBind(); } } else { using (var context = new WOContext()) { context.Report_Types.ToList().ForEach(types => ChangeItemList(DropDownList1, types.ID.ToString(), types.ReportBaseTypes.Name)); } } }
quelle
Ich habe es geschafft, dies mithilfe von Sitzungsvariablen zu erreichen. In meinem Fall wird meine Liste nicht viele Elemente enthalten, sodass sie ziemlich gut funktioniert. So habe ich es gemacht:
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { string[] elems;//Array with values to add to the list for (int q = 0; q < elems.Length; q++) { ListItem li = new ListItem() { Value = "text", Text = "text" }; li.Attributes["data-image"] = elems[q]; myList.Items.Add(li); HttpContext.Current.Session.Add("attr" + q, elems[q]); } } else { for (int o = 0; o < webmenu.Items.Count; o++) { myList.Items[o].Attributes["data-image"] = HttpContext.Current.Session["attr" + o].ToString(); } } }
Wenn die Seite zum ersten Mal geladen wird, wird die Liste gefüllt und ich füge ein Bildattribut hinzu, das nach dem Postback verloren geht :( Wenn ich also die Elemente mit ihren Attributen hinzufüge, erstelle ich eine Sitzungsvariable "attr" plus die Nummer des aufgenommenen Elements aus dem "für" -Zyklus (es wird wie attr0, attr1, attr2 usw. sein) und in ihnen speichere ich den Wert des Attributs (in meinem Fall einen Pfad zu einem Bild), wenn ein Postback auftritt (innerhalb des " sonst ") Ich schleife einfach die Liste und füge das Attribut aus der Sitzungsvariablen hinzu, indem ich das" int "der" for "-Schleife verwende, das dem beim Laden der Seite entspricht (dies liegt daran, dass ich auf dieser Seite keine Elemente hinzufüge Ich hoffe, dies hilft jemandem in der Zukunft, Grüße!
quelle