This is going to be a long one, a lot of code to follow:
Because you have a lot of common elements that you want rendered out, I started with a BaseControl that defined some common methods to generate all the divs you're after. This inherits from System.Web.UI.Control - as the docs state:
This is the primary class that you derive from when you develop custom ASP.NET server controls. Control does not have any user interface (UI) specific features. If you are authoring a control that does not have a UI, or combines other controls that render their own UI, derive from Control.
So the base control looks like this:
/// <summary>
/// Provides some common methods.
/// </summary>
public class BaseControl: Control
{
protected Control[] TempControls;
/// <summary>
/// Clears the child controls explicitly, and stores them locally.
/// </summary>
protected void ClearControls()
{
if (HasControls())
{
TempControls = new Control[Controls.Count];
Controls.CopyTo(TempControls, 0);
}
Controls.Clear();
}
/// <summary>
/// Creates a new panel (HTML div) with the requested CSS
/// and containing any controls passed in.
/// </summary>
/// <param name="cssClass">The CSS class to be applied</param>
/// <param name="controls">Any controls that should be added to the panel</param>
protected Panel NewPanel(string cssClass, params Control[] controls)
{
// Create a new Panel, assign the CSS class.
var panel = new Panel { CssClass = cssClass };
// Loop through the controls adding them to the panel.
foreach (var control in controls)
{
panel.Controls.Add(control);
}
return panel;
}
/// <summary>
/// Creates a new row of panels (HTML div), based on the CSS class prefix.
/// The center panel holds the controls passed in.
/// </summary>
/// <param name="cssClassPrefix"></param>
/// <param name="controls"></param>
protected Panel NewRow(string cssClassPrefix, params Control[] controls)
{
// Expaned for clarity, but could all be passed in on one call.
var row = NewPanel(cssClassPrefix);
row.Controls.Add(NewPanel(cssClassPrefix + "-l"));
row.Controls.Add(NewPanel(cssClassPrefix + "-c", controls));
row.Controls.Add(NewPanel(cssClassPrefix + "-r"));
return row;
}
}
You then need to create some controls that will handle each of your templates, I've created two here.
First up, a control that will generate a single row - used by both the header and footer:
public class AcmeSimple : BaseControl, INamingContainer
{
private string m_CssPrefix;
public AcmeSimple(string cssPrefix)
{
m_CssPrefix = cssPrefix;
}
protected override void CreateChildControls()
{
ClearControls();
Panel wrapper = NewPanel(m_CssPrefix + "-wrapper", TempControls);
Panel simple = NewRow(m_CssPrefix, wrapper);
Controls.Add(simple);
base.CreateChildControls();
}
}
It creates a new panel to hold the controls, and then creates a new row of divs to hold the wrapper.
Then a slightly more complex contents control, that works on the same principle as the header:
public class AcmeContents: BaseControl, INamingContainer
{
protected override void CreateChildControls()
{
ClearControls();
Panel wrapper = NewPanel("panel-body-wrapper", TempControls);
Panel contents = NewPanel("panel-body");
contents.Controls.Add(NewRow("panel-body-t"));
contents.Controls.Add(NewRow("panel-body-m", wrapper));
contents.Controls.Add(NewRow("panel-body-b"));
Controls.Add(contents);
base.CreateChildControls();
}
}
So this one just created three rows, the middle one of which contains the controls.
Finally, the actual control that you place on the page:
[ParseChildren(true)]
[ToolboxData("<{0}:AcmeControl runat=server></{0}:AcmeControl>")]
public class AcmeControl: BaseControl, INamingContainer
{
public bool Scrolling { get; set; }
[TemplateContainer(typeof(AcmeSimple))]
public ITemplate Header { get; set; }
[TemplateContainer(typeof(AcmeContents))]
public ITemplate Contents { get; set; }
[TemplateContainer(typeof(AcmeSimple))]
public ITemplate Footer { get; set; }
protected override void CreateChildControls()
{
Controls.Clear();
string cssClass = "panel";
if (Scrolling)
{
cssClass += " scrollContents";
}
Panel panel = NewPanel(cssClass);
panel.ID = ID;
Controls.Add(panel);
if (Header != null)
{
var header = new AcmeHeader("panel-header");
Header.InstantiateIn(header);
panel.Controls.Add(header);
}
if (Contents != null)
{
var contents = new AcmeContents();
Contents.InstantiateIn(contents);
panel.Controls.Add(contents);
}
else
{
// Possibly a little harsh, as it's a runtime exception.
throw new ArgumentNullException("Contents", "You must supply a contents template.");
}
if (Footer != null)
{
var footer = new AcmeSimple("panel-footer");
Footer.InstantiateIn(footer);
panel.Controls.Add(footer);
}
}
}
So we define the templates that we support as properties of the control, along with the Scrollable property you wanted. Then, in CreateChildControls we start building up the body of the control using the controls we created at the begining and the methods in the BaseControl.
This goes onto the page like this:
<cc1:AcmeControl ID="AcmeControl1" runat="server">
<Header>
<b>Here's a header</b>
</Header>
<Contents>
<i>Here's some controls in the content.</i>
</Contents>
</cc1:AcmeControl>
And renders out like this:
<div id="AcmeControl1_AcmeControl1" class="panel">
<div class="panel-header">
<div class="panel-header-l">
</div>
<div class="panel-header-c">
<div class="panel-header-wrapper">
<b>Here's a header</b>
</div>
</div>
<div class="panel-header-r">
</div>
</div>
<div class="panel-body">
<div class="panel-body-t">
<div class="panel-body-t-l">
</div>
<div class="panel-body-t-c">
</div>
<div class="panel-body-t-r">
</div>
</div>
<div class="panel-body-m">
<div class="panel-body-m-l">
</div>
<div class="panel-body-m-c">
<div class="panel-body-wrapper">
<i>Here's some controls in the content.</i>
</div>
</div>
<div class="panel-body-m-r">
</div>
</div>
<div class="panel-body-b">
<div class="panel-body-b-l">
</div>
<div class="panel-body-b-c">
</div>
<div class="panel-body-b-r">
</div>
</div>
</div>
</div>
So the only difference is that the contents styles are t-l rather than tl.
However (and this could be a big issue for you), template controls aren't really designed to be filled from code-behind - you'll notice that trying to write:
AcmeControl1.Footer.Controls.Add([...]);
won't compile.
What you can do however is call:
AcmeControl1.Footer = Page.LoadTemplate([...])
and pass in the path to an ascx file.
Further reading on creating templated controls can be found:
I said it would be long.
Best Answer
I haven't tested this but I would think you could do this by inheriting from the literal or label control and then reading/writing to the Text property.
p.s. next time when you post a question check the preview to see if it's readable and format code with 4 spaces in-front so it's actually shown and syntax highlighted.