11 out of 36 rated this helpful - Rate this topic

How to: Create Templated ASP.NET User Controls 

You can create user controls that implement templates, an ASP.NET feature that allows the separation of control data from its presentation. A templated control does not provide a user interface. Instead, it is written to implement a naming container and to include a class whose properties and methods are accessible to the host page.

The user interface for the user control is supplied by a page developer at design time. The developer creates templates of the type defined by the user control, and can then add controls and markup to the templates.

To create a templated user control

  1. In the .ascx file, add an ASP.NET PlaceHolder control where you want the template to appear.

  2. In the user control's code, implement a property of type ITemplate.

  3. Define a server control class that implements the INamingContainer interface as a container in which to create an instance of the template. This is called the template's naming container.

    NoteNote

    This control essentially becomes a nested class of the user control, though this is not required.

  4. Apply the TemplateContainerAttribute to the property that implements ITemplate and pass the type of the template's naming container as the argument to the attribute's constructor.

  5. In the control's Init method, repeat the following steps one or more times:

    • Create an instance of the naming container class.

    • Create an instance of the template in the naming container.

    • Add the naming container instance to the Controls property of the PlaceHolder server control.

      NoteNote

      From the point of view of the page using the user control, the syntax for the templated user control is identical to what it would be with a custom templated control.

Example

The following example shows a templated user control and a page that contains it. The user control creates a template that can be declared on a host page as <MessageTemplate>. The template control also exposes two properties, Index and Message, that the host page can access inside the template.

The first sample shows the templated user control. The second sample shows a page that contains the user control.

<%@ Control language="C#" ClassName="TemplatedUC" %>
<script runat=server>
private ITemplate messageTemplate = null;

[ TemplateContainer(typeof(MessageContainer)) ]
public ITemplate MessageTemplate {
    get 
    { 
        return messageTemplate; 
    }
    set 
    { 
        messageTemplate = value; 
    }
}

void Page_Init() {
    if (messageTemplate != null) {
        String[] fruits = {"apple", "orange", "banana", "pineapple" };
        for (int i=0; i<4; i++) 
        {
            MessageContainer container = new MessageContainer(i, fruits[i]);
            messageTemplate.InstantiateIn(container);
            PlaceHolder1.Controls.Add(container);
        }
    }
}

public class MessageContainer: Control, INamingContainer {
    private int m_index;
    private String m_message;
    internal MessageContainer(int index, String message)
    { 
        m_index = index;
        m_message = message;
    }
    public int Index {
        get 
        { 
            return m_index; 
        } 
    }
    public String Message 
    { 
        get 
        { 
            return m_message; 
        } 
    }
}
</script>
<asp:placeholder runat=server id="PlaceHolder1" />


<%@ Page Language="C#" %>
<%@ Register TagPrefix="uc" tagname="TemplateTest" 
    Src="TemplatedUC.ascx" %>
<html>
<script runat=server>
    protected void Page_Load()
    {
        DataBind();
    }
    
</script>
<head>
<title>Templated User Control Test</title>
</head>
<body>
<h1>Testing Templated User Control</h1>
<form id="Form1" runat=server>
<uc:TemplateTest runat=server>
  <MessageTemplate>
    Index: <asp:Label runat="server" ID="Label1" 
        Text='<%# Container.Index %>' />
    <br />
    Message: <asp:Label runat="server" ID="Label2" 
        Text='<%# Container.Message %>' />
    <hr />
  </MessageTemplate>
</uc:TemplateTest>
</form>
</body>
</html>

See Also

Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
All Above suggestions not working for VS2008
I have used the following attributes, but its not working.


[TemplateContainer(typeof(ContentContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
[Browsable(false)]
[DefaultValue((string)null)]
[Description("The content template which contains the inner controls.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

I am getting the error ''Error creating control - .... 'System.Web.UI.UserControl' does not have a public property named 'ContentTemplate'
to get the designer to work..

I've tried all these suggestions to get the designer to work, but to no avail. Is this because of changes in VS 2008. which I'm using?

Does anyone have a VS2008 solution? (there's nothing under the 2008 version of this page).

TemplateInstance Attribute
I found that using the [TemplateInstance(TemplateInstance.Single)] allowed me to access controls within the template. This also made the designer errors go away. http://msdn.microsoft.com/en-us/library/system.web.ui.templateinstance.aspx
to get the designer to work..
simply add the following code above the MessageTemplate Property


<System.ComponentModel.Browsable(False)> _

<TemplateContainer(GetType(MessageContainer), ComponentModel.BindingDirection.TwoWay)> _

<PersistenceMode(PersistenceMode.InnerProperty)> _

Acces to the template's Controls from de Page
To access the controls within the template, take a look at the TemplateInstance attribute. Setting it to TemplateInstance.Single, controls becomme accessible. But note that in this example, the template is instancied 4 times...

Regarding the Designer issue, i did not found any workarround for now. But good dirrection could be looking at the UpdatePanel source code (using reflector). I did not dig deeper for now but it seems to provide a solution...


To access using FindControl, just use a cast
If you want to access controls inside your template, you just need to cast the first control inside your PlaceHolder to your naming container class. Then FindControl will work just fine.

So, for this example, first add an ID for the user control:

<uc:TemplateTest ID="TemplateTest" runat=server>

Then try these statements to find "Label1":

MessageContainer container = (MessageContainer)TemplateTest.FindControl("PlaceHolder1").Controls[0];
Label label = (Label)container.FindControl("Label1")

Hope that helps,

-Dan

Type 'System.Web.UI.UserControl' does not have a public property named 'MessageTemplate'
Ok, the run-time works but the appearance of the 'error' in the design time leads to coutless hours of lost developer time trying to understand the error in order to make sure that one is 'getting it right'. Microsoft needs to provide better documentation on these anomalies.
Still cannot access controls within the Template

I cannot gain access to any controls inside the template. Not even with dgivoni's suggestion of FindControl.

I have copied the code verbatum from the example (and added the PersistenceMode attribute to the Template property) but it's not working for me.

Still no Design View support
Even with Tatham's additional attributes the error reported by mj1856 is still present on Design View- I can't find a workaround does anyone know of one?
Accessing controls within the Template
Using this approach it seems that by code it is no longer possible on the page level to access a control in the template area directly.
Writing f.ex. this.Label1.Text = "ddd" will give a compile time error, but accessing it by writing ((Label) this.TemplateTest.FindControl("Label1")).Text = "ddd" works ok.
But this is horribly long to write and will not catch errors until runtime.
Is there any workaround for this?
Designer Support Fix

Continuing from mj1856's comment, I found this combination of attributes gets it working in the designer:


[TemplateContainer(typeof(MessageContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ITemplate MessageTemplate

Not Quite...

To get the sample to work, you must also specify the InnerProperty PersistenceMode for the template.

For example:

[ TemplateContainer(typeof(MessageContainer)),
PersistenceMode(PersistenceMode.InnerProperty) ]
public ITemplate MessageTemplate {
...

Also - while this approach would be wonderful, it doesn't have design-time support. Implement the exact code here (plus the needed attribute) and then goto design mode on the page. You'll get the following:

Error Creating Control - TemplateTest1

Type 'System.Web.UI.UserControl' does not have a public property named 'MessageTemplate'.

Apparently there is a bug in the designer that only looks at the base class instead of the actual control. I am at a loss on how to work around the bug.