Asp.net user control with inner controls

asp.netweb-user-controls

I need to develop a template-like user control, which would accept any arbitrary content, including other controls.

WmlCard.ascx

<asp:PlaceHolder ID="phContent" runat="server">
<card id="<%= Me.CardId %>" title="<%= Me.Title %>"> 
<p>
    <%= Me.InnerText %>
</p>
</card>
</asp:PlaceHolder>

It would be used this way:
ListaTelefonica.aspx

<%@ Register TagPrefix="sic" TagName="Card" Src="~/path/to/WmlCard.ascx"%>    
<sic:Card ID="BuscaCard" runat="server" CardId="busca" title="Lista TelefĂ´nica" Visible="false">
   <asp:Literal ID="SomeLiteral" runat="server" Visible="false">Some text<br /></asp:Literal>
   <asp:Literal ID="RuntimeFilledLiteral" runat="server" Visible="false" /><br /></asp:Literal>
   Any text.
</sic:Card>

It is unfortunate that Ascx user controls have to inherit System.Web.UI.UserControl. If I could inherit System.Web.UI.WebControls.WebControl, for example, it would be a piece of cake.

My problem is similar to this one, but instead of just text the control should accept any other control inside it.


I tried <ParseChildren(False)> and overriding AddParsedSubObject(Object) in WmlCard, but it is not a solution because the html is rendered before the Page Load, making it pointless to change the value of RuntimeFilledLiteral.Text in a page's Page_Load, for example.

First error I got:
Parser Error Message: Type 'ASP.sic_sicphone_usercontrol_wmlcard_ascx' does not have a public property named 'Literal'.

After adding <PersistenceMode(PersistenceMode.InnerDefaultProperty), ParseChildren(True, "InnerText")>:
The 'InnerText' property of 'sic:Card' does not allow child objects.

Same as above but changing WmlCard's property InnerText type from String to List(Of Object):
Literal content ('Any text.') is not allowed within a 'System.Collections.IList'.

After adding <ParseChildren(False)> and overriding AddParsedSubObject:
No error message, but WmlCard's content is rendered to html before I have a chance to change its inner controls runtime properties.

If I change WmlCard to inherit System.Web.UI.WebControls.PlaceHolder instead of System.Web.UI.UserControl:
'…UserControl.WmlCard' is not allowed here because it does not extend class 'System.Web.UI.UserControl'.

Best Answer

Found a valid way:

1) Put nothing inside my control (WmlCard.ascx).

2) Added the attribute <ParseChildren(False)> to WmlCard.

3) Added a handler to the event PreRender on WmlCard:

Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
  Dim litBefore As New Literal()
  litBefore.Text = String.Format("<card id=""{0}"" title=""{1}""><p>", Me.CardId, Me.Title)

  Dim litAfter As New Literal()
  litAfter.Text = "</p></card>"

  Me.Controls.AddAt(0, litBefore)
  Me.Controls.Add(litAfter)
End Sub

And that's it. Everything is working. Thanks for your help thinking thru this!