views:

451

answers:

3

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?

A: 

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.

Jason Berkan
This isn't enough, because it should be possible to put anything into the control, not only text boxes.
Deniz Dogan
Ah - sorry I missed that part of the question.
Jason Berkan
I realise now that I should have been more clear on that part! :)
Deniz Dogan
+2  A: 

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();
    }
}
Mehdi Golchin
I'm sorry, but I really don't understand how to use this code snippet. What does the .ascx file look like in this case?
Deniz Dogan
I think I understand now, but one question remains... What if I don't want to use ContentTemplate or anything like that?
Deniz Dogan
Why don't you want that?
Mehdi Golchin
A: 

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.

Matthew