71 votes

Les attributs de ListItems dans une DropDownList sont perdus lors du postback ?

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

Il a une liste déroulante 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;
    }

Au chargement de la page, les infobulles des éléments s'affichent, mais au premier retour en arrière, les attributs sont perdus. Pourquoi est-ce le cas, et existe-t-il des solutions de contournement ?

0 votes

Votre code .aspx devrait probablement s'afficher également.

73voto

Laramie Points 2608

J'ai eu le même problème et je voulais contribuer ce où l'auteur a créé un consommateur ListItem hérité pour faire persister les attributs dans ViewState. J'espère que cela permettra à quelqu'un d'économiser le temps que j'ai perdu jusqu'à ce que je tombe dessus par hasard.

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

0 votes

Pourquoi être si énigmatique ? si c'est censé hériter d'un ListItem alors cela ne fonctionne pas

2 votes

Vous devez hériter d'une classe de DropDownList et ensuite l'utiliser, comme gleapman l'a expliqué ci-dessous ;)

2 votes

La solution consiste à créer un nouveau contrôle que je n'aime pas. Il existe un moyen de le faire sans aucune sous-classification.

39voto

gleapman Points 121

Merci, Laramie. C'est exactement ce que je cherchais. Il conserve parfaitement les attributs.

Pour développer, voici un fichier de classe que j'ai créé en utilisant le code de Laramie pour créer une liste déroulante 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 placer le contrôle sur votre formulaire web avec ceci

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

0 votes

Il se peut que vous deviez ajouter l'assemblage à la balise de référence, même s'il s'agit du même assemblage... Je pense que cela dépend si c'est un projet d'application Web ou un site Web. Pour une application Web nommée "MyWebApplication", il faudrait alors lire : <%@ Register Assembly="MyWebApplication" TagPrefix="aspNewControls" Namespace="NewControls"%>

2 votes

J'ai essayé votre solution, mais si j'utilise votre contrôle hérité, il est en quelque sorte inaccessible dans le code derrière. Je veux dire que si j'essaie ddlWhatever.Items il jette l'exception null de la ddlWhatever Vous savez pourquoi ?

0 votes

@david : Cela ne fonctionne pas si vous créez une UserControl et essayer d'hériter de la DropDownList .

14voto

ram Points 41

Une solution simple consiste à ajouter les attributs de l'info-bulle dans le fichier pre-render de la liste déroulante. Toute modification de l'état doit être effectuée à pre-render événement.

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

8voto

Andrew Hare Points 159332

Si vous ne voulez charger les listes d'éléments qu'au premier chargement de la page, vous devez activer ViewState pour que le contrôle puisse sérialiser son état et le recharger lorsque la page est réaffichée.

Il y a plusieurs endroits où ViewState peut être activé - vérifiez le site Web de la Commission européenne. <pages/> dans le web.config et également dans le fichier <%@ page %> en haut du fichier aspx lui-même pour la directive EnableViewState propriété. Ce paramètre devra être true pour que ViewState fonctionne.

Si vous ne souhaitez pas utiliser ViewState, il suffit de supprimer l'élément if (!IsPostBack) { ... } autour du code qui ajoute le ListItems et les éléments seront recréés à chaque postback.

Edit : Je m'excuse - j'ai mal interprété votre question. Vous avez raison de dire que le attributs ne survivent pas au postback car ils ne sont pas sérialisés dans ViewState. Vous devez réintroduire ces attributs à chaque postback.

6voto

Subhash Points 21

Une solution simple : appelez votre fonction de chargement de la liste déroulante lors de l'événement de clic où vous demandez le renvoi.

0 votes

N'oubliez pas de stocker le dropdown.SelectedIndex avant de recharger le dropdown, afin de pouvoir restaurer la sélection de l'utilisateur par la suite.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X