2009-03-13 6 views
2

La page ViewState d'une page ASP.NET semble avoir des problèmes de suivi avec les contrôles supprimés dynamiquement et leurs valeurs.ViewState et les contrôles supprimés de manière dynamique

Prenons le code suivant comme exemple:

ASPX:

<form id="form1" runat="server"> 
<div> 
    <asp:Panel runat="server" ID="controls" /> 
</div> 
</form> 

CS:

protected void Page_Init(object sender, EventArgs e) { 
    Button b = new Button(); 
    b.Text = "Add"; 
    b.Click +=new EventHandler(buttonOnClick); 
    form1.Controls.Add(b); 
    Button postback = new Button(); 
    postback.Text = "Postback"; 
    form1.Controls.Add(postback); 
} 

protected void Page_Load(object sender, EventArgs e) { 
    if (ViewState["controls"] != null) { 
     for (int i = 0; i < int.Parse(ViewState["controls"].ToString()); i++) { 
      controls.Controls.Add(new TextBox()); 
      Button remove = new Button(); 
      remove.Text = "Remove"; 
      remove.Click +=new EventHandler(removeOnClick); 
      controls.Controls.Add(remove); 
      controls.Controls.Add(new LiteralControl("<br />")); 
     } 
    } 
} 

protected void removeOnClick(object sender, EventArgs e) { 
    Control s = sender as Control; 
    //A hacky way to remove the components around the button and the button itself 
    s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s) + 1]); 
    s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s) - 1]); 
    s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s)]); 
    ViewState["controls"] = (int.Parse(ViewState["controls"].ToString()) - 1).ToString(); 
} 

protected void buttonOnClick(object sender, EventArgs e) { 
    if (ViewState["controls"] == null) 
     ViewState["controls"] = "1"; 
    else 
     ViewState["controls"] = (int.Parse(ViewState["controls"].ToString()) + 1).ToString(); 
    controls.Controls.Add(new TextBox()); 
} 

Ensuite, nous allons vous dire créer 4 commandes et insérez le valeurs suivantes:

[ 1 ] [ 2 ] [ 3 ] [ 4 ] 

Nous voulons supprimer le second contrôle; après avoir enlevé la deuxième commande la sortie est:

[ 1 ] [ 3 ] [ 4 ] 

qui est ce que nous voulons. Malheureusement, une publication ultérieure, la liste devient :

[ 1 ] [ ] [ 3 ] 

Alors, ma question est, pourquoi est-ce qui se passe? Pour autant que j'ai lu, ViewState devrait enregistrer les propriétés des contrôles par rapport à leurs index, pas les contrôles réels.

Répondre

6

Les choses en couple. Le fait que les contrôles soient chargés par leur ID ou index dépend de l'attribut ViewStateModeById. Par défaut, il est faux (ce qui signifie charger par index).

Toutefois, les zones de texte sont gérées différemment. Leur état d'affichage ne contient pas de valeur d'entrée sauf s'ils sont désactivés ou invisibles. La propriété text est remplacée par les valeurs affichées en utilisant leurs ID. Puisque vous ne gérez pas les ID de zone de texte, c'est ce qui se passe. Après avoir ajouté quatre contrôles, vous avez quatre zones de texte: ctrl0, ctrl1, ctrl2 et ctrl3 avec les valeurs 1, 2, 3 et 4 respectivement.

Ensuite, vous supprimez la boîte ctrl1 et le client obtient trois zones: ctrl0, ctrl2 et ctrl3 avec des valeurs d'accord. Maintenant, lorsque vous effectuez une publication, ces trois valeurs sont soumises sous la forme ctrl0 = 1 & ctrl2 = 3 & ctrl3 = 4. Puis, sur Page_Load, vous créez trois contrôles, cette fois-ci: ctrl0, ctrl1, ctrl2 sans valeurs.

L'infrastructure appelle LoadRecursive pour charger les états d'affichage, puis ProcessPostData pour affecter des valeurs d'entrée. Il voit soumis ctrl0 et ctrl2, trouve des contrôles avec le même id et leur assigne les valeurs 1 et 3. Il ne trouve pas ctrl3, donc il l'ignore.Le ctrl1 restant continue simplement sans aucune valeur.

À titre d'exemple, considérons cette solution (pas le meilleur):

protected void Page_Init(object sender, EventArgs e) 
{ 
    Button b = new Button(); 
    b.Text = "Add"; 
    b.Click += new EventHandler(buttonOnClick); 
    form1.Controls.Add(b); 

    Button postback = new Button(); 
    postback.Text = "Postback"; 
    form1.Controls.Add(postback); 
} 

protected void Page_Load(object sender, EventArgs e) 
{ 
    if (ViewState["controls"] != null) 
    { 
     List<string> ids = (List<string>)ViewState["controls"]; 

     for (int i = 0; i < ids.Count; i++) 
     { 
      TextBox textbox = new TextBox(); 
      textbox.ID = string.Format("txt_{0}", ids[i]); 
      textbox.Text = textbox.ID; 
      controls.Controls.Add(textbox); 

      Button remove = new Button(); 
      remove.Text = "Remove"; 
      remove.Click += new EventHandler(removeOnClick); 
      remove.ID = ids[i]; 
      controls.Controls.Add(remove); 

      controls.Controls.Add(new LiteralControl("<br />")); 
     } 
    } 
} 

protected void removeOnClick(object sender, EventArgs e) 
{ 
    Control btn = sender as Control; 

    List<string> ids = (List<string>)ViewState["controls"]; 
    ids.Remove(btn.ID); 

    //A hacky way to remove the components around the button and the button itself 
    btn.Parent.Controls.Remove(btn.Parent.Controls[btn.Parent.Controls.IndexOf(btn) + 1]); 
    btn.Parent.Controls.Remove(btn.Parent.Controls[btn.Parent.Controls.IndexOf(btn) - 1]); 
    btn.Parent.Controls.Remove(btn); 

    ViewState["controls"] = ids; 
} 

protected void buttonOnClick(object sender, EventArgs e) 
{ 
    List<string> ids; 

    if (ViewState["controls"] == null) 
     ids = new List<string>(); 
    else 
     ids = (List<string>)ViewState["controls"]; 

    string id = Guid.NewGuid().ToString(); 
    TextBox textbox = new TextBox(); 
    textbox.ID = string.Format("txt_{0}", id); 
    textbox.Text = textbox.ID; 
    controls.Controls.Add(textbox); 

    Button remove = new Button(); 
    remove.Text = "Remove"; 
    remove.Click += new EventHandler(removeOnClick); 
    remove.ID = id; 
    controls.Controls.Add(remove); 

    controls.Controls.Add(new LiteralControl("<br />")); 

    ids.Add(id); 

    ViewState["controls"] = ids; 
} 
+0

J'ai un problème connexe - J'ai une personne à charge gridview sur un postback combo. Lorsque vous modifiez le combo, la grille est actualisée avec les données de la nouvelle valeur de combo. Mais si vous cliquez sur Rafraîchir, la grille retourne aux données par défaut du premier élément de la liste déroulante, mais la liste déroulante est réinitialisée à l'élément sélectionné lors de l'actualisation. La grille et la combinaison ne sont donc pas synchronisées. J'ai essayé de lier et de placer le combo sur le premier élément de 'Page_Load if! Postback', et le html montre que le premier élément est décoré' selected = "selected" ', mais un peu de javascript le change en non-défaut article! Est-ce lié? – Chris

0

Vous effectuez la moitié de votre code dans le Page_Load et la moitié de votre code dans vos événements de publication. Quelque part dans ce gâchis, vous avez des conflits. Le fait qu'ils n'apparaissent pas avant la deuxième publication me dit qu'il y a un peu de logique dans votre logique. Je ne suis pas sûr exactement où le problème se produit, mais la gestion de ViewState n'est pas la chose la plus amusante au monde, quand vous faites customState crud. Je devrais reconstruire l'application et définir les conditions de surveillance pour voir ce qui se passe, mais je suis à peu près sûr qu'il s'agit d'une incompatibilité d'impédance entre votre code Page_Load et les gestionnaires d'événements.

Questions connexes