Laden...

dyn. Usercontrols und Postback - Designfehler?

Erstellt von sra vor 16 Jahren Letzter Beitrag vor 16 Jahren 1.645 Views
S
sra Themenstarter:in
230 Beiträge seit 2004
vor 16 Jahren
dyn. Usercontrols und Postback - Designfehler?

Schon wieder ich...

Ich habe versucht in einer Seite, in der die einzelnen Bereiche voneinander abhängig sind (zb. es gibt eine Liste mit x Einträgen und jeder Eintrag führt - je nach gewähltem Imagebutton - zu einem Formview oder einer weiteren Liste) den Aufbau mit Usercontrols zu lösen.

Dafür habe ich mehrere Panels auf meine aktuelle Seite gezogen, und dieser eine öffentliche Methode zugefügt, der ich beliebige Controls als Parameter mitgeben kann, die es dann in die Panels schreibt.

Public Sub LoadAdditionalContent(ByVal AdditionalContentControl As System.Web.UI.Control) Implements DWA.Interfaces.IAdditionalContent.LoadAdditionalContent
    Me.pnlAdditional.Controls.Clear()
    Me.pnlAdditional.Controls.Add(AdditionalContentControl)
    Me.pnlAdditional.Visible = True
End Sub

Diese Methode platziert das Control dem Panel

Ich dachte mir, ich könne so ganz einfach aus dem einen (User)Control den Inhalt der anderen Bereiche auf de Seite steuern

CType(Me.Page, IAdditionalContent).LoadAdditionalContent(myUsercontrol)

zB. könnte ich vom Usercontrol mit der Liste ganz einfach mit einfach das weiterführende Control mit der entsprechenden Form nachladen

Mein Problem ist nun, dass dieses Control, da es zur Laufzeit erzeugt wird, keinen Postback überlebt. Ich kann es auch nicht einfach neu erstellen, da ich in der Seite selber nicht weiss, welches Control aktuell angezeigt wird (werden sollte).

Nun würde ich gerne von euch wissen, ob dieser Ansatz überhaupt "weiterdenkenswert" ist, oder ob ihr da einfach nur die Köpfe schüttelt. Die Ursprungsidee kommt übrigens von einem Design mit Frames (und ich kann Frames nicht haben!). Ich wollte aber den Code der einzelnen Bereiche getrennt haben. Und mit Updatepanels merkt man gar nichts mehr vom Pagereload.

Gruss
sra

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 16 Jahren

Oder anders gefragt:

Wie kann ich mein Usercontrol über den Pagerefresh hinaus erhalten? Kann ich das Usercontrol in den Viewstate schreiben? Wenn ja, wie geht das mit der Serialisierung von einem Usercontrol?

Gruss
sra

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

5.941 Beiträge seit 2005
vor 16 Jahren

Hallo sra

Ich sehe den Sinn von "AdditionalContent" nicht ganz, aber vielleicht übersehe ich etwas.
Du hast in deiner Methode das Panel pnlAdditionalContent hardcodiert, die Lösung bringt also auch nicht wirklich eine Dynamik und Flexibilität.
Clear() ist nicht nötig, ausser du lädst wärend der Seitenbearbeitung mal dies und mal jenes Control rein (was wiederum keinen Sinn macht~).

Was ist mit: Me.pnlWhatEver.Controls.Add() an der gewünschten Stelle?
Das finde ich einfacher als den Cast der Page auf dein Interface.

Aber evt. übersehe ich etwas 🙂

Original von sra
Wie kann ich mein Usercontrol über den Pagerefresh hinaus erhalten? Kann ich das Usercontrol in den Viewstate schreiben? Wenn ja, wie geht das mit der Serialisierung von einem Usercontrol?

Das UserControl Objekt musst du nicht persistieren, würde ja auch nicht wirklich Sinn machen.
Den Zustand eines UserControls macht hingegen schon Sinn.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 16 Jahren

Wenn ich es ohne das Interface machen würde, so müsste ich this.Page in den Typ der Pageklasse casten. Damit ich diese Klasse nicht wissen muss (da das UC auch in anderen Pages eingebunden sein kann) habe ich das mit Interfaces gelöst.

Eine Methode ist es im Grunde genommen darum, weil ich nicht nur den Inhalt reinlade, sondern auch gleich die Visibility setze und weil es einfach geiler aussieht 😁

Wie gesagt... ich bin mir überhaupt nicht sicher, ob das ein guter Weg ist. Die Vorteile sehe ich vor allem in der Trennung des Codes. Vorher hatte ich drei Gridviews mit je drei Events, was mir einfach zu viel unordnung im Code war. Masterpages bringen auch nicht mehr Trennung des Codes wie das z.B. includes in php machen und Usercontrols schienen mir die ideale Lösung zu sein.

Verstehst du (ihr?), was ich meine?

btw. was versteht man unter persistieren? 🙂

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

5.941 Beiträge seit 2005
vor 16 Jahren

Hallo sra

Original von sra
Wenn ich es ohne das Interface machen würde, so müsste ich this.Page in den Typ der Pageklasse casten. Damit ich diese Klasse nicht wissen muss (da das UC auch in anderen Pages eingebunden sein kann) habe ich das mit Interfaces gelöst.

Ein Interface macht natürlich schon Sinn, jedoch sehe ich denn Sinn deiner Methode nicht wirklich.
Clear() braucht man praktisch nie bis gar nicht und <Control>.Visible hardcodiert auf True zu setzen macht IMO auch kein Sinn, da der Standardwert sowieso true ist.

Original von sra
Wie gesagt... ich bin mir überhaupt nicht sicher, ob das ein guter Weg ist. Die Vorteile sehe ich vor allem in der Trennung des Codes. Vorher hatte ich drei Gridviews mit je drei Events, was mir einfach zu viel unordnung im Code war. Masterpages bringen auch nicht mehr Trennung des Codes wie das z.B. includes in php machen und Usercontrols schienen mir die ideale Lösung zu sein.

Wie oben schon geschrieben, sehe ich bei deiner Verwendung den Sinn leider nicht.
Wenn du das ganze irgendwie weiterspinnen / erweitern würdest, könnte es Sinn machen, je nach Anforderung.

Original von sra
Verstehst du (ihr?), was ich meine?

Ich hoffe es 🙂

Original von sra
btw. was versteht man unter persistieren? 🙂

Ich verstehe darunter, dass man Daten, die sich normalerweise verflüchtigen, an einem Ort (zwischen-)speichert und je nach Bedarf wieder lädt, wiederherstellt.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 16 Jahren

Ich sehe, wir kommen der Sache schon näher 🙂
Ich versuche mal meine Anforderungen zu schildern.

Die ganze Übung hat ein relativ komplexes System zum Ziel, in dem man Ausschreibungen erfassen und editieren kann. Dazu muss man natürlich Personen erfassen und zuordnen können, die widerum bei den Ausschreibungen mitmachen können (eben falls sie zugeordnet sind). Dabei müssen das gewählte Projekt (bzw. die Rechte der Person darauf) und die Kategorie der Person berücksichtigt werden. ... Ich habe also immer Daten, die von "vorherigen" Interaktion basieren. Ich möchte diese nicht immer in einer "neuen" Page darstellen, sondern zusätzlich auf der schon offenen Seite platzieren.

Im Grunde genommen geht es mir darum, dass ich ohne Frames einzelne Bereiche in der Webseite bekomme, die ich individuell "füllen" und "leeren" kann. Ich habe mir deshalb gedacht, ich mache dies einfach mit einigen Panels (für das Aussehen) und darin lade ich mit Updatepanels die Bereiche (Usercontrols) dynamisch nach. Als Beispiel: Ich loge mich ein, und sehe eine Liste mit Ausschreibungen (UC1). Ich klicke bei einem Datensatz auf einen der Buttons und es soll mir im zweiten Panel (pnlAdditionalContent), das bisher usichtbar gesetzt war eine weitere Liste anzeigen (UC2). Das mache ich mit eben dieser LoadAdditionalContent Methode.

Mein Problem: Klicke ich im zweiten UC im darin enthaltenen Gridview auf eine Spalte, um diese zu sortieren, dann ist das UC wieder weg! Ich glaube, das passiert, weil die Seite ja an sich nichts vom UC weiss und bei einem Postback dieses deshalb nicht wieder rendert. Das selbe passiert, wenn ich zb ein Form bearbeiten will etc... (Ich habe eigentlich alle Einzelteile in UCs gesteckt, auch wenn sie eigentlich unkompliziert wären - also im extremfall 1 Formview und 1 ObjectDataSource)

Vielleicht wäre es besser mit Events zu arbeiten. Dann hätte ich aber wieder den Code für 3 Grids mit je 3 abgefangenen Events, was ich ja nicht wollte.

Persistenz hört sich schon mal gut an, allerdings weiss ich nicht genau wie ich das anstellen soll. Wie kann ich der Seite selber klarmachen, welches Control genau dynamisch nachgeladen wurde?

btw. das Clear braucht es wahrscheinlich wirklich nicht, das Visible = True jedoch schon, da das Panel anfangs ausgeblendet ist (und mit UnLoadAdditionalContent wieder wird).

Vielen Dank, dass du dir die Zeit nimmst!!!

Gruss
sra

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

A
154 Beiträge seit 2005
vor 16 Jahren

Wie kann ich mein Usercontrol über den Pagerefresh hinaus erhalten?

Hallo, ich stand vor Kurzem vor dem selben Problem.
Hier mal meine Erfahrungen.

Du musst entweder deine Controls oder deine Daten irgendwo zwischenspeichern.
Hier gibt es verschiedene Wege.

Du packst deine Controls in einem Placeholder.
Beim Verlassen der Seite speicherst du den Inhalt (ControlCollection) des Placeholders in nem Session-Objekt ab.

Bei Laden der Seite holst du dir die Session und fügst die Items der Collection wieder dem Placeholder hinzu.

Das ganze funktioniert ganz gut. Hat aber einen Nachteil. Bei deinen UserControls wird kein PageLoad mehr ausgelößt. D.h. nachdem du die Controls aus der Session wieder in den Placeholder geschoben hast, war es das. Dazu unten mehr.

Wie kommen nun deine Events dennoch an das UserControl?
Du implementierst das IPostBackEventHandler Interface. Beim wiederherstellen rufst du dann RaisePostBackEvent() auf und übergibst request.Params["__EventArgument"].

Oder du schreibst dir ein Interface mit einer Page_Load(object sender, EventArgs e).
Dann rufst du nach dem Laden (PlaceHolder.Controls.Add()) selber die Page_Load auf. Wenn die Page_Load des UserControls aufgerufen wird, sollte das mit den Events wie gewohnt klappen.

Wenn die Page_Load nicht aufgerufen wird, habe ich festgestellt, dass mehrere Dinge dann oft nicht mehr richtige gehen. Z.B. die Selection bei einem DropDownFeld.

Hier ein bischen Code


public class PostbackSecurePlaceholder : System.Web.UI.WebControls.PlaceHolder, IPostBackEventHandler
    {

    #region IPostBackEventHandler Member

    public void RaisePostBackEvent(string eventArgument)
    {
        foreach (Control co in this.Controls)
        {
            try
            {
                ((IPostBackEventHandler)co).RaisePostBackEvent(eventArgument);
            }
            catch { }
        } 	   
    }

    #endregion

        public void Store()
        {
            if (this.ID == null || this.ID.Length == 0)
                throw new Exception("Field Id is required!");

            if (Context.Session[this.ID] == null)
            {
                Context.Session.Add(this.ID, this.Controls);
            }
            else
            {
                Context.Session[this.ID] = this.Controls;
            }
        }

        public void Restore()
        {
            if (Context.Session[this.ID] == null)
            {
                //hmm
            }
            else
            {
                ControlCollection coCol1 = ((ControlCollection)Context.Session[this.ID]);
                while(coCol1.Count > 0)
                {
                    this.Controls.Add(coCol1[0]);
                }

                Context.Session[this.ID] = this.Controls;
            }
        }

        public void Restore(System.Web.HttpRequest request)
        {
            this.Restore(request.Params["__EventTarget"], request.Params["__EventArgument"]);            
        }

        public void Restore(string eventTarget, string eventArgument)
        {
            if (Context.Session[this.ID] == null)
            {
                //hmm
            }
            else
            {
                ControlCollection coCol1 = ((ControlCollection)Context.Session[this.ID]);

                while(coCol1.Count > 0)
                {
                    try
                    {
			            //Diese IfAbfrage macht nur Sinn, wenn du ein Konzept zur Id vergabe hast, ansonsten ohne diesem If.
                        if (eventTarget != null && eventTarget.IndexOf(coCol1[0].ClientID) != -1)
                        {

                            ((IPostBackEventHandler)coCol1[0]).RaisePostBackEvent(eventArgument);
                        }

			            //alternativ könnte ich mir auch sowas vorstellen (Das Interface müßte halt noch erstellt werden.
			            //Dann ersparrt man sich das RaisePostBackEvent und dann das Herausfieseln wohin das Event soll (wie oben versucht zu beschreiben).
			            //Handhabe es mittlerweilse so, ist ein bischen älterer Code hier.
			            //((IPerformPageLoadHandler)coCol1[0]).Page_Load(this, eventArgument));
                    }
                    catch 
                    {
                    }

                    this.Controls.Add(coCol1[0]);
                    
                }

                Context.Session[this.ID] = this.Controls;
            }
        }
    }




So gehst du dann damit in der Seiten-Klasse damit um


UserControls speichern
protected override void OnPreRender(EventArgs e)
{
	this.placeholder1.Store();
...


UserControls wiederherstellen
protected override void OnInit(EventArgs e)
{
	this.placeholder1.Restore(this.Request);
...


//UserControls hinzufügen
placeholder1.Controls.Add(  this.Page.LoadControl("MYControl.ascx") );




.