ASP.NET: User control with access to the controls

2019-08-12 12:45发布

问题:

I have a bunch of occurrences of this kind of boilerplate code in my ASP.NET project.

<div class="inputfield">
  <div class="tl">
    <span class="tr"><!-- --></span>
    <span class="ll"><!-- --></span>
    <div class="lr">
      <div class="cntnt">
        <asp:TextBox .../>
      </div>
    </div>
  </div>
</div>

As you may have guessed, everything in that snippet is pure boilerplate except for the innermost text field.

What is the best way to avoid such boilerplate in ASP.NET? In e.g. Django I would make a custom tag for it, as such:

{% boiler %}
<input ... />
{% endboiler %}

I was thinking that maybe I can create a user control, but all the tutorials on ASP.NET user controls that I've found are very simplistic and "self-closing", i.e. they are not aware of the contents of the tag. I need something along the lines of:

<Hello:MyControl>
  <asp:TextBox .../>
</Hello>

So my question is the following: what's the best way to avoid the boilerplate?

回答1:

You can use an ITemplate property. Thus, you can inject different content in different situations.

[PersistChildren(false), ParseChildren(true, "ContentTemplate")]
public partial class WebUserControl1 : System.Web.UI.UserControl
{

    [System.ComponentModel.Browsable(false), System.Web.UI.PersistenceMode(PersistenceMode.InnerProperty)]
    public ITemplate ContentTemplate { get; set; }

    protected override void CreateChildControls()
    {
        if (this.ContentTemplate != null)
            this.ContentTemplate.InstantiateIn(this);

        base.CreateChildControls();
    }
}


回答2:

Put the asp:TextBox in your user control, along with the other html tags. Provide properties on your user control that match the properties of the text box, so that you would do something like this:

<Hello:MyControl ID="myControl" runat="server" Width="300px" MaxLength="30" />

and then the width and maxlength properties would just get transferred to the internal textbox.

You could also provide access to the textbox from the usercontrol and set all the properties in the code behind.



回答3:

Create a class like this:

[PersistChildren(false), ParseChildren(true, "ContentTemplate")]
public class CustomContent:WebControl
{
    [System.ComponentModel.Browsable(false), System.Web.UI.PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public ITemplate ContentTemplate { get; set; }

    private PlaceHolder m_placeHolder;
    protected override void CreateChildControls()
    {
        m_placeHolder = new PlaceHolder();

        if (this.ContentTemplate != null)
            this.ContentTemplate.InstantiateIn(m_placeHolder);

        Controls.Add(m_placeHolder);
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {
        writer.Write(@"<div class=""inputfield"">
<div class=""tl"">
<span class=""tr""><!-- --></span>
<span class=""ll""><!-- --></span>
<div class=""lr"">
  <div class=""cntnt"">
");
        base.RenderContents(writer);

        writer.Write(@"      </div>
</div>
</div>
</div>
");
    }

}

This class isn't a "User Control" it's a "Server Control". You can do the same thing with a user control but you'll have issues with the designer. This will work in the designer. And you can put markup like this in your ASPX:

    <uc1:CustomContent runat="server" ID="content">
       <asp:textbox runat="server"></asp:textbox>
    </uc1:CustomContent>

don't forget the Register page declaration at the top of the aspx

<%@ Register tagprefix="uc1" Assembly="Assembly where CustomContent is" Namespace="namespace where CustomContent is" %>

You can put whatever you want inside the uc1:CustomContent tags and it will render that boilerplate html around it. If you are curious about how ITemplate works, there are plenty of articles on msdn, etc.