Asp.net user control with inner controls
I need to develop a template-like user control, which would accept any arbitrary content, including other controls.
W开发者_Go百科mlCard.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")>
:
Same as above but changing WmlCard's property InnerText
type from String
to List(Of Object)
:
After adding <ParseChildren(False)>
and overriding AddParsedSubObject
:
If I change WmlCard to inherit System.Web.UI.WebControls.PlaceHolder
instead of System.Web.UI.UserControl
:
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!
What you need is a templated user control.
Take a look at this article for a detailed how-to: http://msdn.microsoft.com/en-us/library/36574bf6.aspx.
Actually you could create a custom user control, inherit from placeholder and your problem should be solved?
You can go the templated control way as suggested but I don't see the reason why you would want to do that.
[SupportsEventValidation, DefaultEvent("YourEventName")]
[ParseChildren(true)]
[PersistChildren(false)]
[ToolboxBitmap(typeof(System.Web.UI.WebControls.Panel))]
public class MyCustomControl : System.Web.UI.WebControls.PlaceHolder, INamingContainer, IPostBackDataHandler, IPostBackEventHandler
you can override the Render method to control the rendered HTML. I am struggling to see youru actual problem.
Got this working simply enough.
[ParseChildren(false)]
public class CssControl : System.Web.UI.UserControl
{
protected override void Render(HtmlTextWriter writer)
{
string html = null;
using(var innerWriter = new System.IO.StringWriter())
using(var htmlWriter = new HtmlTextWriter(innerWriter))
{
base.Render(htmlWriter);
html = innerWriter.GetStringBuilder().ToString();
}
var min = ScriptAndCssParser.MinifyCssFromHtml(html);
writer.Write(min);
}
}
In my case my goal was allow a master page level control to have child HTML and server-controls, like asp:content
blocks. That way Viewpages could pass stylesheets up the chain to the master page.
Then I wanted to intercept all the generated HTML, and minify the CSS files. The minification part is out of scope for this question, but the above ParseChildren(false)
prevents the "element can't contain..." error in the .aspx, and then calling base.Render()
on an inner writer allows you to use the existing Web Forms pipeline to do all the rendering for you of both server-controls and plain HTML content.
精彩评论