How to create an ASP.Net control that behaves as a template container to nest content via markup?

Category:
ASP.net
Publishdate:
24.07.2011
Hits:
12780

Usage

The following article will show you how you can create a UserControl that allows you to share a common layout between several controls, that differ in their content. (The principle is the same as using MasterPages/ContentPages)

By doing so, you will remove redundent layout markup from your UserControls that have the same appearence, like UserControls in a sidebar, that differ in the headlines and the content.
One major advantage of using templated UserControls is that adjusting/changing the layout can be done in a central place.

Walkthrough on how to refactor several controls to use just one control as template container

Let's assume you have some sidebar controls, that look like the following.

This is how the example code will look like in the end

You see that they differ in the headline and the content, but other than that, they have the same layout.

One way to create the two UserControls would be to create and style one, copy the markup and paste it into the other control. Let's assume the markup for the first UserControl looks like this:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyControl1.ascx.cs"
    Inherits="TemplateContainer.MyControl1" %>
<div class="sidebarOuter">
    <div class="sidebarTop">
        Headline 1
    </div>
    <div class="sidebarVBar">
    </div>
    <div class="sidebarContent">
        Lorem ipsum dolor sit amet, consetetur ...
    </div>
    <div class="clear">
    </div>
</div>

The other UserControl would basically contain the same markup, just with "Headline 2" and another text.

Step 1

The first step in this tutorial will be to replace the Headline String with a Literal, that get's bound on the Page_Load:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyControl1.ascx.cs"
    Inherits="TemplateContainer.MyControl1" %>
<div class="sidebarOuter">
    <div class="sidebarTop">
        <asp:Literal ID="litHeadline" runat="server" />
    </div>
    <div class="sidebarVBar">
    </div>
    <div class="sidebarContent">
        Lorem ipsum dolor sit amet, consetetur ...
    </div>
    <div class="clear">
    </div>
</div>
public partial class MyControl1 : System.Web.UI.UserControl
{
	public String Headline { get; set; }
	
	protected void Page_Load(object sender, EventArgs e)
	{
		this.litHeadline.Text = this.Headline;
	}
}

Usage:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="TemplateContainer._Default" %>
<%@ Register Src="~/MyControl1.ascx" TagName="MyControl1" TagPrefix="uc" %>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
        <uc:MyControl1 ID="myControl1" runat="server" Headline="Headline 1" />
</asp:Content>

Step 2

We will now replace the content part with a TemplateContainer, that allows us to host any kind of markup. In the markup we will a PlaceHolder Control. Somewhere in the code we need a new class, of whose type the TemplateContainer will be. For our layout purpose this will be just a empty class, that implements INamingContainer and inherits from Control. For this tutorial, I chose to place the class in the same file.

The most important addition to the codebehind file will be the property Content, which is also the name of the tags that should store the markup. There are several attributes assigned, whose purpose you can easily look up in the MSDN.

This is the code after the second step:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyControl1.ascx.cs"
    Inherits="TemplateContainer.MyControl1" %>
<div class="sidebarOuter">
    <div class="sidebarTop">
        <asp:Literal ID="litHeadline" runat="server" />
    </div>
    <div class="sidebarVBar">
    </div>
    <div class="sidebarContent">
        <asp:PlaceHolder ID="placeHolderContent" runat="server" />
    </div>
    <div class="clear">
    </div>
</div>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace TemplateContainer
{
    public partial class MyControl1 : System.Web.UI.UserControl
    {
        public String Headline { get; set; }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            this.CreateContainer();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            this.litHeadline.Text = this.Headline;
        }

        [TemplateContainer(typeof(ContentContainer)),
PersistenceMode(PersistenceMode.InnerDefaultProperty),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
TemplateInstance(TemplateInstance.Single)]
        public ITemplate Content { get; set; }

        protected void CreateContainer()
        {
            if (Content != null)
            {
                ContentContainer container = new ContentContainer();
                Content.InstantiateIn(container);
                this.placeHolderContent.Controls.Add(container);
            }
        }

    }

    public class ContentContainer : Control, INamingContainer
    {
    }
}

Usage:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="TemplateContainer._Default" %>
<%@ Register Src="~/MyControl1.ascx" TagName="MyControl1" TagPrefix="uc" %>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
  <uc:MyControl1 ID="myControl1" runat="server" Headline="Headline 1">
    <content>
       Lorem ipsum dolor sit amet, consetetur sadipscing elitr ...
    </content>
  </uc:MyControl1>
</asp:Content>

Result

Basically that was all it took to create a templated control. My control only contained basic html/css, but it should be clear that you can upscale as much as you want.

For more detailed information on templated controls you should check the MSDN or leave a comment and request more details.

And finally here's the full markup and the css I used for this example:

Complete Usage:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="TemplateContainer._Default" %>
<%@ Register Src="~/MyControl1.ascx" TagName="MyControl1" TagPrefix="uc" %>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
	<uc:MyControl1 ID="myControl1" runat="server" Headline="Headline 1">
		<Content>
			Lorem ipsum dolor sit amet, consetetur sadipscing elitr ...
		</Content>
	</uc:MyControl1>
	<uc:MyControl1 ID="myControl2" runat="server" Headline="Headline 2">
		<Content>
			Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum
		</Content>
	</uc:MyControl1>
</asp:Content>

CSS:

body
{
    background-color: #D3D3D3;
}
.sidebarOuter
{
    width: 250px;
    background-color: #FFFFFF;
    margin-bottom: 20px;
}

.sidebarTop
{
    width: 240px;
    float: left;
    padding: 5px;
    font-size: 18px;
    font-weight: bold;
}
.sidebarVBar
{
    width: 240px;
    margin-left: 5px;
    height: 2px;
    background-color: #696969;
    float: left;
}
.sidebarContent
{
    width: 240px;
    float: left;
    padding: 5px;
}
.clear
{
    clear: both;
}


Comments:

Berry - 25.05.2012 - 16:13
Thanks for great article! Could You please explain how could I add such a custom control in code behind? The point is, that i don't know the number of controls I need at the moment, it is defined by a number of rows in table, so I was going to use foreach statement. How could I instance my custom control there and define it's content?

amit - 04.05.2012 - 12:32
hey Matthias,

This is very useful article,
can you please tell me how can i create component
which will simply add to my page using their .dll

like ajex toolkit extender works

Chyna - 16.08.2011 - 03:18
Created the greatest artciles, you have.

Add a comment (due to recent massive spam, I've decided to only display new comments after a manual spam check):
Name
E-Mail (will not be displayed)  
Text