Question

Un collègue de travail m'a montré ceci:

Il a un DropDownList et un bouton sur une page Web. Voici le code derrière:

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

Sur la charge de la page, les infobulles des articles sont affichés, mais la première publication, les attributs sont perdus. Pourquoi est-ce le cas, et sont-il des solutions de contournement?

Était-ce utile?

La solution

J'ai eu le même problème et je voulais contribuer cette ressource où l'auteur a créé un consommateur ListItem hérité de persister des attributs à ViewState. Espérons que cela va sauver quelqu'un le temps que je perdu jusqu'à ce que je suis tombé là-dessus.

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

Autres conseils

Merci, Laramie. Exactement ce que je cherchais. Il conserve les attributs parfaitement.

Pour se développer, ci-dessous est un fichier de classe I créés à l'aide du code de Laramie pour créer un dropdownlist dans VS2008. Créez la classe dans le dossier App_Code. Après avoir créé la classe, utilisez cette ligne sur la page ASPX pour l'enregistrer:

<%@ Register TagPrefix="aspNewControls" Namespace="NewControls"%>

Vous pouvez ensuite mettre le contrôle sur votre formulaire en ligne avec ce

<aspNewControls:NewDropDownList ID="ddlWhatever" runat="server">
                                                </aspNewControls:NewDropDownList>

Ok, voici la classe ...

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

Une solution simple est d'ajouter les attributs d'info-bulles dans l'événement pre-render de la liste déroulante. Toute modification de l'état doit être fait au cas de pre-render.

exemple de code:

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

Si vous voulez seulement charger les listitems sur la première charge de la page, vous devez activer ViewState afin que le contrôle peut sérialiser son état là-bas et le recharger lorsque les messages de page en arrière.

Il y a plusieurs endroits où ViewState peut être activé - Vérifiez le noeud <pages/> dans le web.config et aussi dans la directive <%@ page %> en haut du fichier ASPX lui-même pour la propriété EnableViewState. Ce paramètre devra être true pour ViewState travailler.

Si vous ne souhaitez pas utiliser ViewState, il suffit d'enlever le if (!IsPostBack) { ... } de partout dans le code qui ajoute le ListItems et les articles seront recréés sur chaque publication.

Modifier Je suis désolé - j'ai mal lu votre question. Vous avez raison que les attributs ne survivent pas postback car ils ne sont pas dans ViewState sérialisés. Vous devez ajouter à nouveau ces attributs sur chaque publication.

Un simple, Solution- Appelez votre liste déroulante fonction de chargement sur l'événement click où vous demande de retour après.

Les solutions typiques à ce problème implique la création de nouveaux contrôles qui ne sont pas tout à fait réalisable dans des circonstances normales. Il existe une solution simple et triviale à ce problème.

Le problème est que la ListItem perd ses attributs sur postback. Cependant, la liste elle-même ne perd jamais des attributs personnalisés. On peut tirer profit de cette manière simple et efficace ainsi.

Étapes:

  1. Sérialisez vos attributs à l'aide du code dans la réponse ci-dessus ( https://stackoverflow.com/a/3099755/3624833 )

  2. Magasin à un attribut personnalisé de la ListControl (dropdownlist, checklistbox, peu importe).

  3. après retour, relue l'attribut personnalisé de la ListControl et désérialiser puis retour sous forme d'attributs.

Voici le code que je l'habitude (de) attributs sérialisation (ce que je devais faire était de garder une trace de ce que les éléments de la liste ont été initialement rendus comme sélectionnés lors de la récupération du back-end, puis enregistrer ou supprimer des lignes selon la les modifications apportées par l'utilisateur sur l'interface utilisateur):

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

(ci-dessus, "Les utilisateurs" est un contrôle CheckboxList).

après retour (dans mon cas un événement bouton Soumettre Click), j'utilise le code ci-dessous pour récupérer les mêmes et les stocker dans un dictionnaire pour le traitement post:

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: J'ai funcs bibliothèque qui effectuent des conversions de gestion des erreurs et de données, en omettant la même chose ici par souci de concision)

Voici le code VB.Net de la solution proposée par Laramie et raffinée par gleapman.

Mise à jour: Le code ci-dessous j'ai posté est en fait pour le contrôle ListBox. Il suffit de changer l'héritage à DropDownList et renommer la classe.

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 = 1
            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
                For Each attribute As String() In DirectCast(myState(i), String()())
                    li.Attributes(attribute(0)) = attribute(1)
                    i += 1
                Next
            Next
        End If
    End Sub
End Class
End Namespace

@Sujay Vous pouvez ajouter un texte séparé point-virgule dans l'attribut de valeur de la liste déroulante (comme le style csv), et utiliser String.Split ( « ; ») pour obtenir 2 « valeurs » sur une valeur, comme solution pour sortir sans avoir à créer de nouveau le contrôle de l'utilisateur. Surtout si vous avez seulement quelques attributs supplémentaires, et si elle est pas trop long. Vous pouvez également utiliser un attribut valeur JSON dans la valeur de la liste déroulante, puis analyser à tout ce que vous avez besoin à partir de là.

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

Une solution simple sans ViewState, la création de nouveaux contrôle serveur ou complexe smth:

Création:

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

Mise à jour:

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

Exemple:

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

J'ai réussi à y parvenir en utilisant des variables de session, dans mon cas ma liste ne va pas contenir de nombreux éléments de sorte qu'il fonctionne assez bien, voici comment je l'ai fait:

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

Lorsque la page est chargée première fois que la liste est renseignée et j'ajouter un attribut d'image qui est perdu après la publication :( donc au moment où ajouter les éléments avec ses attributs créer une variable de session « attr » plus le nombre de l'élément pris du cycle « pour » (ce sera comme attr0, attr1, attr2, etc ...) et les je sauvegarder la valeur de l'attribut (un chemin vers une image dans mon cas), lorsque survient postback ( à l'intérieur du « autre ») Je viens de boucler la liste et ajoutez l'attribut pris de la variable de session en utilisant le « int » de la boucle « pour » qui est le même que lorsque la page a été chargée (car dans ce que je fais pas ajouter des éléments à la liste sélectionnant simplement ils ont toujours le même indice) et les attributs sont définis à nouveau, j'espère que cela aide quelqu'un à l'avenir, salutations!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top