2009-01-09 4 views
5

UPDATE:Ich habe mit meinen Kontrollen etwas mehr experimentiert und ich denke, dass ich näher gekommen bin. Bitte lesen Sie weiter für aktualisierte Informationen.Warum werden untergeordnete Elemente meines benutzerdefinierten Benutzersteuerelements nicht initialisiert?

Ich habe 2 ASP.NET 2.0 Benutzersteuerelemente, von denen eine innerhalb des anderen platziert wird. Innerhalb der inneren Kontrolle habe ich eine HtmlAnchor Tag-Kontrolle, die ich versuche, eine URL für die äußere Kontrolle festzulegen. Wenn ich versuche, die HRef -Eigenschaft von der Wiedergabeseite Markup festzulegen, ist das HtmlAnchor Steuerelement null und löst eine Ausnahme aus.

Die URL set Eigenschaft ist vor entweder den inneren dem OnInit Ereignis aufgerufen wird oder die äußeren Kontrolle und vor der Haupt Seite ‚OnPreInit‘ Ereignis. Ich nehme an, dass das ist, weil ich die URL des Kindsteuerelements im Markup des Elternsteuerelements auf der Seite setze, auf der ich die Steuerelemente rendere, was bedeutet, dass der Wert vor OnInit() gesetzt wird. Hier ist die (abgespeckte) Version des Codes Ich verwende:

[ParseChildren(true)]// <- Setting this to false makes the 
        // page render without an exception 
        // but ignores anything in the markup. 
[PersistChildren(false)] 
public partial class OuterControl : System.Web.UI.UserControl 
{ 
    // The private '_Inner' control is declared 
    // in the .ascx markup of the control 
    [PersistenceMode(PersistenceMode.InnerProperty)] 
    public InnerControl Inner 
    { 
     get{ return _Inner; } 
     set{ _Inner = value; } 
    } 
} 

public partial class InnerControl : System.Web.UI.UserControl 
{ 
    // The private 'linkHref' control is declared 
    // in the .ascx markup of the control 
    public string Url 
    { 
     get { return linkHref.HRef; } 
     set { linkHref.HRef = value; } 
    } 
} 

Die OuterControl auf meiner Seite Default.aspx wie folgt verwendet:

<uc1:OuterControl ID="OuterCtrl1" runat="server"> 
    <Inner Url="#" /> 
</uc1:OuterControl> 

Im Markup-Beispiel oben, wenn Ich versuche, diese Seite eine Ausnahme auszulösen wird geworfen, weil das Steuerelement null ist. Mit meinem Debugger kann ich sehen, dass jedes Steuerelement innerhalb der InnerControl ist Null, aber beide InnerControl & OuterControl OnInit() Ereignis wurde noch nicht ausgelöst, wenn die Url Eigenschaft zugegriffen wird.

UPDATE
Ich dachte, die Attribute 'ParseChildren' und 'PersistChildren' helfen würde hinzufügen. Ich habe sie zuvor in den Serversteuerelementen verwendet, aber nie in den Benutzersteuerelementen, obwohl der Effekt ähnlich zu sein scheint. Ich denke nicht, dass ich die Dokumentation für diese beiden Eigenschaften richtig interpretiere, aber es kann verhindern, dass Ausnahmen ausgelöst werden. Die Seitenmarkierung wird jedoch ignoriert.

Kennt jemand einen Weg, um diese Arbeit zu haben. Ich verstehe nicht, warum diese Steuerelemente Werte erhalten, die vor OnInit() festgelegt sind. Wenn ich versuche, ihre Werte mithilfe des ASPX-Markups festzulegen, wird der Konstruktor für InnerControl zweimal aufgerufen. Einmal, um die Werte basierend auf dem Markup (ich nehme an) und wieder auf OnInit() (was ich vermute, warum die Markup-Werte ignoriert werden).

Ist diese Anstrengung hoffnungslos oder komme ich gerade aus dem falschen Winkel?

Antwort

0

Setzen Sie HRef auf die OnInit-Methode der Seite? Wenn dies der Fall ist, verschieben Sie die Zuweisung nach Page_Load.

Die Steuerelemente Init von der äußersten zu den innersten. Das heißt, wenn Sie den Wert auf OnInit der Seite zuweisen, wurden die Steuerelemente noch nicht initialisiert.

Hier ist ein gutes Dokument auf Seite Lebenszyklus: http://www.codeproject.com/KB/aspnet/lifecycle.aspx

+0

Ich setze HRef in den Markup der Seite, nicht die OnInit-Methode. Ich weiß, dass dies vor dem Ereignis OnPreInit() der Page geschieht. Ich möchte in der Lage sein, das Markup zu verwenden, um Kontrollwerte zu setzen, wenn es möglich ist, da es bedeutet, dass ich nicht neu kompilieren muss, um Änderungen vorzunehmen. –

0

Sie sagte:

// The private 'linkHref' control is declared 
// in the .ascx markup of the control 

Hat es etwas ändern, wenn Sie dies ein geschützter Kontrolle machen?

0

Der Kontrollbaum wird nach Init() erstellt; In der Tat ist Init() der Ort, an dem Sie Ihre Steuerelemente in den Baum einfügen, bevor der Viewstate deserialisiert wird. Sie können nicht auf linkHref zugreifen, bevor die Steuerelementstruktur erstellt wurde.

Eine mögliche Lösung ist, die URL zu speichern, bis Sie sie verwenden können. Innerhalb der inneren Kontrolle, die Eigenschaft URL ändern, um einen einfachen String:

public string Url { get; set; } 

Im Laden oder PreRender Ereignishandler der inneren Kontrolle, propagiert die Zeichenfolge in dem (jetzt initialisiert) linkHref Objekt:

linkHref.HRef = value; 
2

Ich hatte ein ähnliches Problem mit dem Chaos der verschachtelten Kontrollen konfrontiert, so fand ich mich begierig zu helfen, und ich markiert dies auch als Favorit, weil ich es mochte.

reproduziert ich das Problem wie folgt: -

ein Benutzersteuer Erstellt genannt Inner:

Inner.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Inner.ascx.cs" Inherits="Inner" %> 
<a runat="server" id="linkHref">I'm inner</a> 

Inner.ascx.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

public partial class Inner : System.Web.UI.UserControl 
{ 
    public string Url 
    { 
     get 
     { 
      return this.linkHref.HRef; 
     } 
     set 
     { 
      this.linkHref.HRef = value; 
     } 
    } 
} 

Erstellt a Benutzersteuerung genannt Außen:

Outer.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Outer.ascx.cs" Inherits="Outer" %> 
<%@ Register src="Inner.ascx" tagname="Inner" tagprefix="uc1" %> 
<uc1:Inner ID="_Inner" runat="server" /> 

Outer.ascx.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

public partial class Outer : System.Web.UI.UserControl 
{ 
    [PersistenceMode(PersistenceMode.InnerProperty)] 
    public Inner Inner 
    { 
     get 
     { 
      return this._Inner; 
     } 
    } 
} 

Dann eine Seite namens Standard erstellt:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> 

<%@ Register src="Outer.ascx" tagname="Outer" tagprefix="uc1" %> 
<%@ Reference Control="~/Inner.ascx" %> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
    <title></title> 
</head> 
<body> 
    <form id="form1" runat="server"> 
    <div> 
     <uc1:Outer ID="Outer1" runat="server"> 
      <Inner Url="http://google.com" /> 
     </uc1:Outer> 
    </div> 
    </form> 
</body> 
</html> 

Mit all diesen, die Seite "Standard" läuft gut.

Was finde ich seltsam in Ihrem Code ist diese Zeile:

public InnerControl Inner 
    { 
     //... 
     set{ _Inner = value; } 
    } 

Was ist der Punkt, einen Setter für die innere Kontrolle zu schaffen? Ich kann es nicht verstehen. Die innere Kontrollinstanz soll aus dem Markup im äußeren Steuerelement erstellt werden, und es ist diese erstellte Instanz, deren HTML-Anker manipuliert werden soll. Wenn ich diese Zeilen in der Inneren Eigenschaft in der Outer.ascx.cs

set 
{ 
    this._Inner = (ASP.inner_ascx)value; 
} 

hinzufügen werde ich eine NULL-Verweis Ausnahme wie im ursprünglichen Fall erhalten. Ich denke, dass der Setter den ASP.Net-Seitenersteller anweist, eine weitere innere Kontrollinstanz zu erstellen und sie mithilfe der Inner-Eigenschaft festzulegen. Ich bin mir nicht sicher, aber wenn Sie Zeit haben, können Sie genau wissen, wie dies geschieht, indem Sie die vom Seitengenerator generierten cs-Dateien untersuchen, die sich in den temporären ASP.Net-Dateien befinden.

1

Wenn OuterControl.ascx sieht aus wie

<%@ Register TagPrefix="uc1" TagName="InnerControl" Src="InnerControl.ascx" %> 
<uc1:InnerControl runat="server" ID="_Inner" /> 

dann das Problem, dass die OuterControl.Inner Eigenschaft eine set Accessor hat. Löschen Sie die set Accessor das Problem zu beheben:

[PersistenceMode(PersistenceMode.InnerProperty)] 
public InnerControl Inner 
{ 
    get { return _Inner; } 
} 

Erläuterung: Wenn OuterControl.Inner hat eine set Accessor ...

  1. Zuerst ASP.NET instanziiert ASP.outercontrol_ascx (die dynamisch generierte Klasse, die von ableitet OuterControl,), die ihrerseits eine Instanz von ASP.innercontrol_ascx (die dynamisch generierte Klasse, die von InnerControl abgeleitet ist) instanziiert.
  2. Als nächstes weil die Inner Eigenschaft eine set Accessor hat, erstellt ASP.NET eine neue Instanz von InnerControl (nichtASP.innercontrol_ascx, wie man erwarten könnte) und versucht, seine Url Eigenschaft "#" zu initialisieren. Natürlich wirft dies NullReferenceException, weil von ASP.innercontrol_ascx, nicht von InnerControl erstellt wird.
  3. schließlich (wenn keine Ausnahme ausgelöst worden ist), setzt ASP.NET Inner auf die Instanz von InnerControl in Schritt 2.

erstellt Wenn OuterControl.Inner keinen set Accessor hat, wird in Schritt 2, ASP.NET setzt einfach OuterCtrl1.Inner.Url auf "#", und Schritt 3 wird übersprungen.

Hinweis: Einige andere Antworten geben an, dass Steuerelemente in Init erstellt oder initialisiert werden. Das ist falsch; In Markup deklarierte Steuerelemente werden sehr früh im Seitenlebenszyklus in FrameworkInitialize (der vor PreInit auftritt) erstellt und initialisiert.