Problem mit dynamischen Steuerelementen in .NET

Problem mit dynamischen Steuerelementen

Hallo alle,

Ich möchte einige dynamische Steuerelemente erstellen und sie ihren Ansichtszustand über Seitenlasten hinweg beibehalten. Einfach genug, oder? Alles, was ich tun muss, ist, die Steuerelemente bei jedem Seitenladevorgang mit den gleichen IDs neu zu erstellen. HOWEVER, hier ist der Catch - in meinem PreRender-Ereignis möchte ich die Steuerelementauflistung löschen und dann die dynamischen Steuerelemente mit neuen Werten neu erstellen. Die Gründe dafür sind kompliziert, und es würde mich wahrscheinlich über eine Seite oder so brauchen, um zu erklären, warum ich es tun möchte. Also, im Interesse der Kürze, nehmen wir einfach an, dass ich dies unbedingt tun muss und dass es keinen anderen Weg gibt.

Das Problem tritt auf, nachdem ich die Steuerelemente in meinem PreRender-Ereignis neu erstellt habe. Die neu erstellten Steuerelemente binden niemals an den Ansichtszustand, und ihre Werte bleiben nicht über Seitenladungen hinweg erhalten. Ich verstehe nicht, warum das passiert. Ich erstelle die Steuerelemente in meinem OnLoad-Ereignis bereits neu. Wenn ich dies tue, binden die neu erstellten Steuerelemente an den ViewState, vorausgesetzt, dass ich jedes Mal die gleichen IDs verwende. Wenn ich jedoch versuche, dasselbe im PreRender-Ereignis zu tun, schlägt es fehl.

In jedem Fall ist hier mein Beispielcode :

namespace TestFramework.WebControls {

public class ValueLinkButton : LinkButton
{
    public string Value
    {
        get
        {
            return (string)ViewState[ID + "vlbValue"];
        }

        set
        {
            ViewState[ID + "vlbValue"] = value;
        }
    }
}

public class TestControl : WebControl
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        Controls.Clear();

        ValueLinkButton tempLink = null;

        tempLink = new ValueLinkButton();
        tempLink.ID = "valueLinkButton";
        tempLink.Click += new EventHandler(Value_Click);

        if (!Page.IsPostBack)
        {
            tempLink.Value = "old value";
        }

        Controls.Add(tempLink);
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);

        ValueLinkButton tempLink = ((ValueLinkButton)FindControl("valueLinkButton"));  //[CASE 1]

        //ValueLinkButton tempLink = new ValueLinkButton();  [CASE 2]

        tempLink.ID = "valueLinkButton";
        tempLink.Value = "new value";
        tempLink.Text = "Click";            

        Controls.Clear();
        Controls.Add(tempLink);
    }

    void Value_Click(object sender, EventArgs e)
    {
        Page.Response.Write("[" + ((ValueLinkButton)sender).Value + "]");
    }
}

}

Also, lassen Sie uns Fall 1 untersuchen, wo die Zeile neben [CASE 1] nicht auskommentiert wird, aber die Zeile neben [CASE 2] auskommentiert wird. Hier funktioniert alles gut. Wenn ich dieses Steuerelement auf eine Seite lege und die Seite lade, wird ein Link mit der Aufschrift "Klicken" angezeigt. Wenn ich auf den Link klicke, gibt die Seite den Text "[neuer Wert]" aus, und in der nächsten Zeile sehen wir den bekannten Link "Klicken". Jedes Mal, wenn ich auf den Link "Klicken" klicke, sehen wir dasselbe. So weit so gut.

Aber jetzt betrachten wir Fall 2, wo die Zeile neben [CASE 1] auskommentiert wird, aber die Zeile neben [CASE 2] nicht auskommentiert wird. Hier stoßen wir auf Probleme. Wenn wir die Seite laden, sehen wir den Link "Klicken". Wenn ich jedoch auf den Link klicke, gibt die Seite den Text "[]" anstelle von "[neuer Wert]" aus. Das Click-Ereignis wird normal ausgelöst. Der Text "neuer Wert", den ich dem Value-Attribut des Steuerelements zugewiesen habe, wird jedoch nicht beibehalten. Wieder einmal ist mir das ein Rätsel. Wie kommt es, wenn ich das Steuerelement in OnLoad neu erstelle, ist alles in Ordnung und Dandy, aber wenn ich das Steuerelement in PreRender neu erstelle, wird der Wert nicht beibehalten?

Ich habe das Gefühl, dass es einfach einen Weg geben muss, dies zu tun. Wenn ich das Steuerelement in PreRender neu erstelle, gibt es eine Möglichkeit, das neu erstellte Steuerelement an den ViewState zu binden?

Habe ich seit Tagen damit zu kämpfen. Jede Hilfe, die Sie mir geben können, wird geschätzt.

Danke.

Antwort auf "Problem mit dynamischen Steuerelementen in .NET " 9 von antworten

Ich erstelle die Steuerelemente bereits in meinem OnLoad-Ereignis neu.

Das ist Ihr Problem. OnLoad kommt zu spät. Verwenden Sie stattdessen Init.

Vielen Dank für Ihre Hilfe, aber ich habe das versucht und es hat keinen Unterschied gemacht. Außerdem funktioniert OnLoad genauso gut für dynamische Steuerelemente wie OnInit, solange Sie Ihren Steuerelementen jedes Mal die gleichen IDs geben.

Ich glaube, dass, sobald Sie die dynamischen Steuerelemente zur Seite in PageLoad hinzugefügt haben, der ViewState an die Steuerelemente gebunden ist und das Flag "ViewState muss noch gebunden werden müssen" (im Konzept kein tatsächliches Flag) gelöscht wird. Wenn Sie dann die Steuerelemente neu erstellen, ist der vorhandene ViewState nicht mehr gebunden.

Ich sah mich im letzten Jahr etwas Ähnliches gegenüber, nur in meinem Fall wollte ich nicht wollen, dass der ViewState neu bindt. Mein Problem ist, dass ich nicht war, um die vorherigen Steuerelemente nachzubilden, weshalb ich denke, dass der Pseudo-Flag-Begriff oben gilt.

Versuchen Sie, Page.RegisterRequiresControlState(). You can also use anzurufen. Sie können auch RequiresControlState() to check if it's already been registered. verwenden, um zu überprüfen, ob es bereits registriert ist.

Wie andere haben Aussage müssen Sie sicherstellen, dass Sie über die Init-Methode erstellen. Weitere Informationen zum ASP.NET-Seitenlebenszyklus finden Sie in diesem Artikel: http://msdn.microsoft.com/en-us/library/ms178472.aspx

ViewState funktioniert auf der Seite und ihren untergeordneten Objekten. Das neue Steuerelement in [Fall 2] wurde der Seite (oder einem ihrer untergeordneten Elemente) nicht hinzugefügt. Im Falle des obigen Codes ist das Objekt nicht mehr in reichweite, sobald die OnPreRender-Methode endet, und wird Garbage Collection.

Wenn Sie das Steuerelement unbedingt austauschen müssen, müssen Sie das alte Steuerelement mithilfe der Remove()-Methode aus dem übergeordneten Steuerelement entfernen und das neue Steuerelement mit AddAt() an der richtigen Stelle hinzufügen.

Wenn das Steuerelement das einzige Untergeordnete des übergeordneten Elements wäre, wäre der Code in etwa wie das folgende.

ValueLinkButton tempLink = new ValueLinkButton();
Control parent = FindControl("valueLinkButton").Parent;
parent.Remove(FindControl("valueLinkButton"));
parent.AddAt(0, tempLink);

ViewState-unterstützte Eigenschaften werden nur dann in ViewState beibehalten, wenn das Steuerelement derzeit ViewState nachverfolgt. Dies ist beabsichtigt, ViewState so klein wie möglich zu halten: Es sollte nur Daten enthalten, die wirklich dynamisch sind. Das Ergebnis ist:

ViewState-Eigenschaften, die während des Init-Ereignisses festgelegt wurden, sind nicht und werden an ViewState zurückgesetzt (da die Seite noch nicht mit der Verfolgung von ViewState begonnen hat). Init ist daher ein guter Ort, um Steuerelemente hinzuzufügen und (a) Eigenschaften festzulegen, die sich nicht zwischen Postbacks (ID, CssClass...) sowie Anfangswerten für dynamische Eigenschaften ändern (die dann durch Code im Rest des Seitenlebenszyklus geändert werden können - Laden, Ereignishandler, PreRender).

Beim dynamischen Hinzufügen von Steuerelementen in Load oder PreRender wird ViewState nachverfolgt. Der Entwickler kann dann wie folgt steuern, welche Eigenschaften für dynamisch hinzugefügte Steuerelemente beibehalten werden:

  • Eigenschaften, die festgelegt wurden, bevor das Steuerelement der Steuerelementstruktur der Seite hinzugefügt wird, werden nicht in ViewState beibehalten. In der Regel legen Sie Eigenschaften fest, die nicht dynamisch sind (ID usw.), bevor Sie der Steuerelementstruktur ein Steuerelement hinzufügen.

  • Eigenschaften, die festgelegt wurden, nachdem das Steuerelement der Steuerelementstruktur der Seite hinzugefügt wurde, werden in ViewState beibehalten (ViewState-Tracking ist vor dem Load-Ereignis bis nach dem PreRender-Ereignis aktiviert).

In Ihrem Fall legt Ihr PreRender-Handler Eigenschaften fest, bevor er das Steuerelement zur Steuerelementstruktur der Seite hinzufügt. Um das gewünschte Ergebnis zu erhalten, legen Sie dynamische Eigenschaften fest, nachdem Sie das Steuerelement zur Steuerelementstruktur hinzugefügt haben: .

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    ValueLinkButton tempLink = new ValueLinkButton(); // [CASE 2]        
    tempLink.ID = "valueLinkButton"; // Not persisted to ViewState
    Controls.Clear();
    Controls.Add(tempLink);
    tempLink.Value = "new value";  // Persisted to ViewState
    tempLink.Text = "Click";       // Persisted to ViewState
}

Control hinzugefügt, bevor SaveViewState-Methode aufgerufen in Control Lebenszyklus sollte ihre Werte beibehalten. Ich würde Joes Antwort zutun. Überprüfen Sie dieses Bild

http://emanish.googlepages.com/Asp.Net2.0Lifecycle.PNG

Ich habe gestern herausgefunden, dass Sie Ihre App tatsächlich wie normal funktionieren lassen können, indem Sie den Steuerbaum direkt nach dem Loadviewstateevent laden. Wenn Sie das loadviewstate-Ereignis überschreiben, mybase.loadviewstate aufrufen und dann Ihren eigenen Code einfügen, um die Steuerelemente direkt danach neu zu generieren, sind die Werte für diese Steuerelemente beim Laden der Seite verfügbar. In einer meiner Apps verwende ich ein Ansichtszustandsfeld, um die ID oder die Arrayinformationen zu enthalten, die zum Neuerstellen dieser Steuerelemente verwendet werden können.

Protected Overrides Sub LoadViewState(ByVal savedState As Object)
    MyBase.LoadViewState(savedState)
    If IsPostBack Then
        CreateMyControls()
    End If
End Sub