Udostępnij za pośrednictwem


Templated Server Control Example

This example shows a control named VacationHome that demonstrates how to implement a templated server control. The VacationHome control defines two exposed properties, Title and Caption. The page designer sets the values of these properties at design time and the control uses those property values at run time to set properties for its child controls. By editing the <Template> element in the control, the page developer specifies the controls and markup that define the control's user interface. The control also enables page developers to use the <%# Container %> syntax, so that Title and Caption values can be referred to in the template markup at design time and displayed in the rendered output. A page designer could create an ASP.NET Web page that looks like this:

<aspSample:VacationHome ID="VacationHome1" 
  Title="Condo for Rent in Hawaii"  
  Caption="Ocean view starting from $200" 
  Runat="server" Width="230px" Height="129px">
  <Template>
    <table bgcolor="aqua" align="center" id="Table1" 
      runat="server" style="width: 286px; height: 260px">
      <tr>
        <td style="width: 404px" align="center">
          <asp:Label ID="Label1" Runat="server"
            Text="<%#Container.Title%>" 
             Font-Names="Arial, Helvetica"></asp:Label>
        </td>
      </tr>
      <tr>
        <td style="width: 404px">
        <asp:Image ID="Image1" Runat="server" 
          ImageUrl="~/images/hawaii.jpg" />
        </td>
      </tr>
      <tr>
        <td style="width: 404px; height: 26px;" align="center">
          <asp:Label ID="Label2" Runat="server" 
            Text="<%#Container.Caption%>" 
            Font-Names="Arial, Helvetica">
          </asp:Label>
        </td>
      </tr>
    </table>
  </Template>
</aspSample:VacationHome>

Code Listing for the VacationHome Control

Option Strict OnImports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.Design

Namespace Samples.AspNet.VB.Controls
    < _
    AspNetHostingPermission(SecurityAction.Demand, _
        Level:=AspNetHostingPermissionLevel.Minimal), _
    AspNetHostingPermission(SecurityAction.InheritanceDemand, _
        Level:=AspNetHostingPermissionLevel.Minimal), _
    Designer(GetType(VacationHomeDesigner)), _
    DefaultProperty("Title"), _
    ToolboxData( _
        "<{0}:VacationHome runat=""server""> </{0}:VacationHome>") _
    > _
    PublicClass VacationHome
        Inherits CompositeControl
        Private _template As ITemplate
        Private _owner As TemplateOwner

        < _
        Bindable(True), _
        Category("Data"), _
        DefaultValue(""), _
        Description("Caption") _
        > _
        PublicOverridableProperty Caption() AsStringGetDim s AsString = CStr(ViewState("Caption"))
                If s IsNothingThen s = String.Empty
                Return s
            EndGetSet(ByVal value AsString)
                ViewState("Caption") = value
            EndSetEndProperty

        < _
        Browsable(False), _
        DesignerSerializationVisibility( _
            DesignerSerializationVisibility.Hidden) _
        > _
        PublicReadOnlyProperty Owner() As TemplateOwner
            GetReturn _owner
            EndGetEndProperty

        < _
        Browsable(False), _
        PersistenceMode(PersistenceMode.InnerProperty), _
    DefaultValue(GetType(ITemplate), ""), _
    Description("Control template"), _
        TemplateContainer(GetType(VacationHome)) _
        > _
        PublicOverridableProperty Template() As ITemplate
            GetReturn _template
            EndGetSet(ByVal value As ITemplate)
                _template = value
            EndSetEndProperty

        < _
        Bindable(True), _
        Category("Data"), _
        DefaultValue(""), _
        Description("Title"), _
        Localizable(True) _
        > _
        PublicProperty Title() AsStringGetDim s AsString = CStr(ViewState("Title"))
                If s IsNothingThen s = String.Empty
                Return s
            EndGetSet(ByVal value AsString)
                ViewState("Title") = value
            EndSetEndPropertyProtectedOverridesSub CreateChildControls()
            Controls.Clear()
            _owner = New TemplateOwner()

            Dim temp As ITemplate = _template
            If temp IsNothingThen
                temp = New DefaultTemplate
            EndIf

            temp.InstantiateIn(_owner)
            Me.Controls.Add(_owner)
        EndSubPublicOverridesSub DataBind()
            CreateChildControls()
            ChildControlsCreated = TrueMyBase.DataBind()
        EndSubEndClass

    <ToolboxItem(False)> _
    PublicClass TemplateOwner
        Inherits WebControl
    EndClass

#Region "DefaultTemplate"NotInheritableClass DefaultTemplate
        Implements ITemplate

        Sub InstantiateIn(ByVal owner As Control) _
            Implements ITemplate.InstantiateIn
            Dim title AsNew Label
            AddHandler title.DataBinding, AddressOf title_DataBinding
            Dim linebreak AsNew LiteralControl("<br/>")
            Dim caption AsNew Label
            AddHandler caption.DataBinding, _
                AddressOf caption_DataBinding
            owner.Controls.Add(title)
            owner.Controls.Add(linebreak)
            owner.Controls.Add(caption)
        EndSubSub caption_DataBinding(ByVal sender AsObject, _
            ByVal e As EventArgs)
            Dim source As Label = CType(sender, Label)
            Dim container As VacationHome = _
                CType(source.NamingContainer, VacationHome)
            source.Text = container.Caption
        EndSubSub title_DataBinding(ByVal sender AsObject, _
            ByVal e As EventArgs)
            Dim source As Label = CType(sender, Label)
            Dim container As VacationHome = _
                CType(source.NamingContainer, VacationHome)
            source.Text = container.Caption
        EndSubEndClass
#End Region


    PublicClass VacationHomeDesigner
        Inherits ControlDesigner

        PublicOverridesSub Initialize(ByVal Component As IComponent)
            MyBase.Initialize(Component)
            SetViewFlags(ViewFlags.TemplateEditing, True)
        EndSubPublicOverloadsOverridesFunction GetDesignTimeHtml() AsStringReturn"<span>This is design-time HTML</span>"EndFunctionPublicOverridesReadOnlyProperty TemplateGroups() As TemplateGroupCollection
            GetDim collection AsNew TemplateGroupCollection
                Dim group As TemplateGroup
                Dim template As TemplateDefinition
                Dim control As VacationHome

                control = CType(Component, VacationHome)
                group = New TemplateGroup("Item")
                template = New TemplateDefinition(Me, "Template", control, "Template", True)
                group.AddTemplateDefinition(template)
                collection.Add(group)
                Return collection
            EndGetEndPropertyEndClassEndNamespace
// VacationHome.csusing System;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.Design;

namespace Samples.AspNet.CS.Controls
{
    [
    AspNetHostingPermission(SecurityAction.InheritanceDemand, 
        Level=AspNetHostingPermissionLevel.Minimal),
    AspNetHostingPermission(SecurityAction.Demand,
        Level = AspNetHostingPermissionLevel.Minimal),
    Designer(typeof(VacationHomeDesigner)),
    DefaultProperty("Title"),
    ToolboxData(
        "<{0}:VacationHome runat=\"server\"> </{0}:VacationHome>"),
    ]
    publicclass VacationHome : CompositeControl
    {
        private ITemplate templateValue;
        private TemplateOwner ownerValue;

        [
        Bindable(true),
        Category("Data"),
        DefaultValue(""),
        Description("Caption")
        ]
        publicvirtualstring Caption
        {
            get
            {
                string s = (string)ViewState["Caption"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["Caption"] = value;
            }
        }

        [
        Browsable(false),
        DesignerSerializationVisibility(
            DesignerSerializationVisibility.Hidden)
        ]
        public TemplateOwner Owner
        {
            get
            {
                return ownerValue;
            }
        }

        [
        Browsable(false),
        PersistenceMode(PersistenceMode.InnerProperty),
        DefaultValue(typeof(ITemplate), ""),
        Description("Control template"),
        TemplateContainer(typeof(VacationHome))
        ]
        publicvirtual ITemplate Template
        {
            get
            {
                return templateValue;
            }
            set
            {
                templateValue = value;
            }
        }

        [
        Bindable(true),
        Category("Data"),
        DefaultValue(""),
        Description("Title"),
        Localizable(true)
        ]
        publicvirtualstring Title
        {
            get
            {
                string s = (string)ViewState["Title"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["Title"] = value;
            }
        }

        protectedoverridevoid CreateChildControls()
        {
            Controls.Clear();
            ownerValue = new TemplateOwner();

            ITemplate temp = templateValue;
            if (temp == null)
            {
                temp = new DefaultTemplate();
            }

            temp.InstantiateIn(ownerValue);
            this.Controls.Add(ownerValue);
        }

        publicoverridevoid DataBind()
        {
            CreateChildControls();
            ChildControlsCreated = true;
            base.DataBind();
        }

    }

    [
    ToolboxItem(false)
    ]
    publicclass TemplateOwner : WebControl
    {
    }

    #region DefaultTemplate
    sealedclass DefaultTemplate : ITemplate
    {
        void ITemplate.InstantiateIn(Control owner)
        {
            Label title = new Label();
            title.DataBinding += new EventHandler(title_DataBinding);

            LiteralControl linebreak = new LiteralControl("<br/>");

            Label caption = new Label();
            caption.DataBinding 
                += new EventHandler(caption_DataBinding);

            owner.Controls.Add(title);
            owner.Controls.Add(linebreak);
            owner.Controls.Add(caption);

        }

        void caption_DataBinding(object sender, EventArgs e)
        {
            Label source = (Label)sender;
            VacationHome container = 
                (VacationHome)(source.NamingContainer);
            source.Text = container.Caption;
        }

        void title_DataBinding(object sender, EventArgs e)
        {
            Label source = (Label)sender;
            VacationHome container = 
                (VacationHome)(source.NamingContainer);
            source.Text = container.Title;
        }
    }
    #endregion


   publicclass VacationHomeDesigner : ControlDesigner
   {

        publicoverridevoid Initialize(IComponent Component)
        {
            base.Initialize(Component);
            SetViewFlags(ViewFlags.TemplateEditing, true);
        }

        publicoverridestring GetDesignTimeHtml()
        {
            return"<span>This is design-time HTML</span>";
        }

        publicoverride TemplateGroupCollection TemplateGroups
        {
            get {
                TemplateGroupCollection collection = new TemplateGroupCollection();
                TemplateGroup group;
                TemplateDefinition template;
                VacationHome control;

                control = (VacationHome)Component;
                group = new TemplateGroup("Item");
                template = new TemplateDefinition(this, "Template", control, "Template", true);
                group.AddTemplateDefinition(template);
                collection.Add(group);
                return collection;
            }
        }
    }

}

Code Discussion

A templated control extends the CompositeControl by adding a property of type ITemplate and defining the naming container for the control. By defining a naming container, you allow the page developer to use the <#%Container%> syntax in the template definition. The template control also defines a property of a type that derives from Control to host the controls defined in the template. Specific attributes and member overrides are implemented to coordinate the template property, host control, and naming container behavior.

The following list summarizes the main implementation requirements for a templated control as demonstrated by VacationHome. Details about each requirement are provided in the discussion following the list. The VacationHome control demonstrates:

  • Deriving from the CompositeControl base class. A templated control is a special kind of composite control. You can also derive from WebControl, but CompositeControl adds the implementation for INamingContainer, which enables use of the <#%Container%> syntax.

  • Implementing a property of type ITemplate and applying relevant metadata attributes to it to define its persistence and its naming container.

  • Exposing a property of type Control or a class derived from Control that serves to host the controls defined in the template element. This control is referred to as the template container.

  • Overriding the CreateChildControls method to instantiate the template controls in the Controls collection of the template container.

  • Optionally, defining a default template, which the control uses when the page developer does not specify a template.

  • Optionally, defining a designer class for the control. The designer class allows the page developer to edit the templates in a visual designer.

The attributes applied to the ITemplate property are BrowsableAttribute, PersistenceModeAttribute, and TemplateContainerAttribute. The TemplateContainerAttribute specifies the type of the control that the page parser should use when resolving the Container variable in an expression such as <#%Container.Title%> in a template. The type specified must implement INamingContainer and define the data properties (in this case Caption and Title) for the control. This type can be the type of the template owner or of a control further up the control tree. In the VacationHome control, the control whose type is passed into the TemplateContainerAttribute constructor is not the template owner, but the VacationHome control itself. The BrowsableAttribute is set to false, because templates are typically not edited in a visual designer's property editing window. The PersistenceModeAttribute is set to InnerProperty, because the template specification is written as an inner element of the control.

The templated control must define a property of type Control that becomes the container for the controls created by the template. In the example, the VacationHome control defines the Owner property, which is of type TemplateOwner, which in turn derives from WebControl. The TemplateOwner class is marked with ToolboxItem(false) to indicate that the TemplateOwner class does not need toolbox support in a visual designer. For more information, see ToolboxItemAttribute. Controls in the template are instantiated and added to the Controls property of the Owner control. If your control exposes multiple ITemplate properties, you might define a separate template container property for each template. The Owner property is exposed as a public property. This allows the page designer to use the FindControl method to reference specific controls in the template at run time.

The VacationHome control overrides the base CreateChildControls method. The CreateChildControls method instantiates the controls specified in the Template property and adds them to the Owner object's Controls collection. The Owner object is then added to the Controls collection of the VacationHome instance, and the control can then be rendered.

If the page developer has not defined a template, then the VacationHome creates an instance of DefaultTemplate, which derives from ITemplate. The InstantiateIn method creates two Label controls to display the Title and Caption properties. And event-handler method is created for the DataBinding event of each control. The DataBinding event-handler sets the Text property to the appropriate property (Title or Caption) of VacationHome.

The VacationHomeDesigner class that implements a designer for the VacationHome class derives from ControlDesigner. During initialization, the SetViewFlags method, called with TemplateEditing, enables template editing at design time. The GetDesignTimeHtml method is overridden to render the control when it is not in template editing mode. The code in the overridden TemplateGroups property defines one template group that contains one template. Each TemplateGroup object adds a template editing choice in the visual designer's template-editing user interface. (In Visual Studio 2005, template-editing choices are displayed in a smart tag associated with the control.) In the VacationHome control, the only editing choice is "Item". Each TemplateDefinition object creates a template for editing in the designer. The templatePropertyName parameter of the TemplateDefinition constructor specifies the name of the template property in the control. The DesignerAttribute is applied to the VacationHome class to specify the designer class.

Test Page for the VacationHome Control

The following example shows an .aspx page that uses the VacationHome control. The first instance of the control in the page specifies a template for the ITemplate property of the control. The second instance does not specify the ITemplate property, which causes the VacationHome control to use its default template at run time.

<%@ Page Language="VB"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    Sub Page_Load(ByVal sender AsObject, ByVal e As EventArgs)
        IfNot IsPostBack Then
            VacationHome1.DataBind()
            VacationHome2.DataBind()
        EndIfEndSub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>
      VacationHome Control Test Page
    </title>
  </head>
  <body>
    <form id="form1" runat="server">
    <aspSample:VacationHome ID="VacationHome1" 
      Title="Condo for Rent in Hawaii"  
      Caption="Ocean view starting $200" 
      Runat="server" Width="230px" Height="129px">
    <Template>
      <table id="TABLE1" runat="server" 
        style="width: 286px; height: 260px; 
        background-color:Aqua; text-align:center">
         <tr>
          <td style="width: 404px" align="center">
            <asp:Label ID="Label1" Runat="server" 
              Text="<%#Container.Title%>" 
              Font-Names="Arial, Helvetica"></asp:Label>
          </td>
        </tr>
        <tr>
          <td style="width: 404px">
            <asp:Image ID="Image1" Runat="server" 
              ImageUrl="~/images/hawaii.jpg" 
              AlternateText="Hawaii home" />
          </td>
        </tr>
        <tr>
          <td style="width: 404px; height: 26px;" align="center">
            <asp:Label ID="Label2" Runat="server" 
              Text="<%#Container.Caption%>" 
              Font-Names="Arial, Helvetica">
            </asp:Label>
          </td>
        </tr>
      </table>
     </Template>
    </aspSample:VacationHome>  
    <br /> <br />
      <br />
    The VacationHome control rendered with its default template:
    <br /> <br />
    <aspSample:VacationHome ID="VacationHome2" 
      Title="Condo for Rent in Hawaii" 
      Caption="Ocean view starting $200" 
      Runat="server" BorderStyle="Solid" BackColor="#66ffff" 
      Height="30px" Width="238px" Font-Names="Arial, Helvetica" />
    </form>
  </body>
</html>
<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
  void Page_Load(object sender, EventArgs e)
  {
    if (!IsPostBack)
    {
      VacationHome1.DataBind();
      VacationHome2.DataBind();
    }
  }

</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>
      VacationHome Control Test Page
    </title>
  </head>
  <body>
    <form id="form1" runat="server">
    <aspSample:VacationHome ID="VacationHome1" 
      Title="Condo for Rent in Hawaii"  
      Caption="Ocean view starting $200" 
      Runat="server" Width="230px" Height="129px">
    <Template>
      <table id="TABLE1" runat="server" 
        style="width: 286px; height: 260px; 
        background-color:Aqua; text-align:center">
        <tr>
          <td style="width: 404px" align="center">
            <asp:Label ID="Label1" Runat="server" 
              Text="<%#Container.Title%>" 
              Font-Names="Arial, Helvetica"></asp:Label>
          </td>
        </tr>
        <tr>
          <td style="width: 404px">
            <asp:Image ID="Image1" Runat="server" 
              ImageUrl="~/images/hawaii.jpg" 
              AlternateText="Hawaii home" />
          </td>
        </tr>
        <tr>
          <td style="width: 404px; height: 26px;" align="center">
            <asp:Label ID="Label2" Runat="server" 
              Text="<%#Container.Caption%>" 
              Font-Names="Arial, Helvetica">
            </asp:Label>
          </td>
        </tr>
      </table>
     </Template>
    </aspSample:VacationHome>  
    <br /> <br />
      <br />
    The VacationHome control rendered with its default template:
    <br /> <br />
    <aspSample:VacationHome ID="VacationHome2" 
      Title="Condo for Rent in Hawaii" 
      Caption="Ocean view starting $200" 
      Runat="server" BorderStyle="Solid" BackColor="#66ffff" 
      Height="30px" Width="238px" Font-Names="Arial, Helvetica" />
    </form>
  </body>
</html>

Building and Using the Example

For information about building the control and using it in a page, see Building the Custom Server Control Examples. You must add a reference to the System.Design assembly for compilation.

See Also

Concepts

Composite Web Control Example

Typed Styles for Child Controls Example

Other Resources

Developing Custom ASP.NET Server Controls