开发者

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")>:

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'.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜