This question has been asked before
but it doesn't hurt to ask it again:
How do i add templating to a
UserControl
in ASP.net?
Start with a new UserControl
5, which i'll call Contoso
:
public partial class Contoso: System.Web.UI.UserControl
{
}
This will allow us to use a new control:1
<Contoso>
Stuff in here
<Contoso>
Create a public ContentTemplate
property of type ITemplate
:
public partial class Contoso: System.Web.UI.UserControl
{
public ITemplate ContentTemplate { get; set; }
}
and add an indeterminate number of attributes to the ContentTemplate
property:2
//[ParseChildren(true)]
[ParseChildren(true, "ContentTemplate")]
//[ParseChildren(false)]
public partial class Contoso: System.Web.UI.UserControl
{
[TemplateContainer(typeof(ContentContainer))]
[TemplateInstance(TemplateInstance.Single)]
[PersistenceMode(PersistenceMode.InnerProperty)]
//[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
[Browsable(true)]
//[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
//[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ITemplate ContentTemplate { get; set; }
}
this will allow us to add <ContentTemplate>
to the control in our aspx file:1
<Contoso>
<ContentTemplate>
Stuff in here
</ContentTemplate>
</Contoso>
Next we need to actually use the ContentTemplate
stuff, by adding it somewhere. We do this by adding it to one of our UserControl's internal div
elements.
Starting from our .aspx
file which was originally empty:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Contoso.aspx.cs" Inherits="Contoso" %>
we add a parent div
that will hold our ContentTemplate
stuff:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Contoso.aspx.cs" Inherits="Contoso" %>
<div id="ContentDiv" runat="server"></div>
Then we stuff the ContentTemplate
stuff into that parent div
during the control's Init:
public partial class Contoso: System.Web.UI.UserControl
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//If there's content, then put it into our ContentDiv div
if (this.ContentTemplate != null)
this.ContentTemplate.InstantiateIn(ContentDiv);
}
[PersistenceModeAttribute(PersistenceMode.InnerProperty)]
[TemplateInstanceAttribute(TemplateInstance.Single)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ITemplate ContentTemplate { get; set; }
}
Edit: Indicate that your class implements INamingContainer
:
public partial class Contoso: System.Web.UI.UserControl: INamingContainer
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//If there's content, then put it into our ContentDiv div
if (this.ContentTemplate != null)
this.ContentTemplate.InstantiateIn(ContentDiv);
}
[PersistenceModeAttribute(PersistenceMode.InnerProperty)]
[TemplateInstanceAttribute(TemplateInstance.Single)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ITemplate ContentTemplate { get; set; }
}
The INamingContainer
interface does not have any members, and is only used to mark your UserControl
class as something.
And we're done3. We can now use this control in our aspx page. But first we need to "register" it at the top of our aspx page:
<%@ Register src="Contoso.ascx" TagName="Contoso" tagprefix="uc" %>
Where:
Contoso.ascx
is the name of the ascx
fileContoso
is the name of the element we will use to reference this user controluc
is a bit of text we will have to put in front of uc:Contoso
(i use uc
as short for user-control)Add the control to our page:
<uc:Contoso ID="Crackers" runat="server">
<ContentTemplate>
Stuff goes here
</ContentTemplate>
</qwerty:Contoso>
And we're done!4
Edit: Forgot to add the reason the above doesn't work. Visual Studio shows the error:
Error Creating Control - Crackers
Type 'System.Web.UI.UserControl' does not have a public property named 'ContentTemplate'
Which makes sense, since UserControl
does not have a public property named ContentTemplate
- so i can hardly blame it.
This question is one in the ongoing Stackoverflow series, "Templating user controls":
Well I believe you almost got it.
BTW. The UserControl is not rendered using Visual Studio Designer, however when you run the application the control works. This is different if you use Server Controls instead, in that case, the control is displayed correctly in the Visual Studio designer
The following code works great to build templated user controls and templated server controls however, if you would like to add binding capabilities, the process is slightly different, take a look
UserControl
.public class MyTemplateContainer : Control, INamingContainer { }
protected void Page_Load(object sender, EventArgs e)
{
// just to demonstrate using the contorl
this.WebUserControl1.Controls.Add(new LiteralControl("<br />new control"));
}
<%@ Register src="WebUserControl.ascx" tagname="WebUserControl" tagprefix="uc1" %>
<uc1:WebUserControl ID="WebUserControl1" runat="server">
<ContentTemplate>
My Template<br />
<asp:Label Text='Hello People' runat="server" ID="lblMessage" />
</ContentTemplate>
</uc1:WebUserControl>
public partial class WebUserControl : System.Web.UI.UserControl
{
[TemplateContainer(typeof(MyTemplateContainer))]
[TemplateInstance(TemplateInstance.Single)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ITemplate ContentTemplate { get; set; }
protected void Page_Init(object sender, EventArgs e)
{
this.myPlaceHolderTag.Controls.Clear();
if (this.ContentTemplate != null)
{
var container = new MyTemplateContainer();
this.ContentTemplate.InstantiateIn(container);
this.myPlaceHolderTag.Controls.Add(container);
}
else
{
this.myPlaceHolderTag.Controls.Add(new LiteralControl("No template defined"));
}
}
}
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl.ascx.cs" Inherits="WebUserControl" %>
<asp:PlaceHolder runat="server" ID="myPlaceHolderTag" />
<%@ Register Namespace="MyControls" TagPrefix="my" %>
<my:MyServerControl runat="server" ID="myServerControl">
<ContentTemplate>
My Server templated control<br />
<asp:Label Text="My Label" runat="server" />
</ContentTemplate>
</my:MyServerControl>
namespace MyControls
{
[ToolboxItem(false)]
public class MyTemplateContainer : Control, INamingContainer { }
}
namespace MyControls
{
[ToolboxData("<{0}:MyServerControl runat=server >")]
[ToolboxItem(true)]
[ParseChildren(true)]
// you can inherit from another control if you like, for example from the CompositeControl
public class MyServerControl : Control, INamingContainer
{
[TemplateInstance(TemplateInstance.Multiple)]
[TemplateContainer(typeof(MyTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[DefaultValue(null)]
public ITemplate ContentTemplate { get; set; }
protected override void CreateChildControls()
{
var p = new Panel { ID = "myPanel", BackColor = Color.Silver, Width = new Unit("100%") };
if (this.ContentTemplate == null)
{
p.Controls.Add(new LiteralControl("No content has been specified"));
}
else
{
var c = new MyTemplateContainer();
this.ContentTemplate.InstantiateIn(c);
p.Controls.Add(c);
}
this.Controls.Clear();
this.Controls.Add(p);
}
public override void DataBind()
{
this.CreateChildControls();
this.ChildControlsCreated = true;
base.DataBind();
}
public override ControlCollection Controls
{
get
{
this.EnsureChildControls();
return base.Controls;
}
}
}
}
References: