Ejemplo de control de servidor con plantilla

Actualización: noviembre 2007

En este ejemplo se muestra un control denominado VacationHome que describe cómo implementar un control de servidor con plantilla. El control VacationHome define dos propiedades, Title y Caption. Mediante la edición del elemento Template en el control, el desarrollador de páginas especifica los controles y el formato que definen la interfaz de usuario del control. El control también permite al desarrollador de páginas utilizar la sintaxis <#% Container%>, a fin de que Title y Caption puedan mostrarse en la interfaz de usuario. El diseñador de páginas podría crear una página Web ASP.NET que presentara el aspecto siguiente:

<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" 
       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>

Lista de código del control VacationHome

Option Strict On
Imports 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>") _
    > _
    Public Class VacationHome
        Inherits CompositeControl
        Private _template As ITemplate
        Private _owner As TemplateOwner

        < _
        Bindable(True), _
        Category("Data"), _
        DefaultValue(""), _
        Description("Caption") _
        > _
        Public Overridable Property Caption() As String
            Get
                Dim s As String = CStr(ViewState("Caption"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("Caption") = value
            End Set
        End Property

        < _
        Browsable(False), _
        DesignerSerializationVisibility( _
            DesignerSerializationVisibility.Hidden) _
        > _
        Public ReadOnly Property Owner() As TemplateOwner
            Get
                Return _owner
            End Get
        End Property

        < _
        Browsable(False), _
        PersistenceMode(PersistenceMode.InnerProperty), _
    DefaultValue(GetType(ITemplate), ""), _
    Description("Control template"), _
        TemplateContainer(GetType(VacationHome)) _
        > _
        Public Overridable Property Template() As ITemplate
            Get
                Return _template
            End Get
            Set(ByVal value As ITemplate)
                _template = value
            End Set
        End Property

        < _
        Bindable(True), _
        Category("Data"), _
        DefaultValue(""), _
        Description("Title"), _
        Localizable(True) _
        > _
        Public Property Title() As String
            Get
                Dim s As String = CStr(ViewState("Title"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("Title") = value
            End Set
        End Property


        Protected Overrides Sub CreateChildControls()
            Controls.Clear()
            _owner = New TemplateOwner()

            Dim temp As ITemplate = _template
            If temp Is Nothing Then
                temp = New DefaultTemplate
            End If

            temp.InstantiateIn(_owner)
            Me.Controls.Add(_owner)
        End Sub

        Public Overrides Sub DataBind()
            CreateChildControls()
            ChildControlsCreated = True
            MyBase.DataBind()
        End Sub


    End Class

    <ToolboxItem(False)> _
    Public Class TemplateOwner
        Inherits WebControl
    End Class

#Region "DefaultTemplate"
    NotInheritable Class DefaultTemplate
        Implements ITemplate

        Sub InstantiateIn(ByVal owner As Control) _
            Implements ITemplate.InstantiateIn
            Dim title As New Label
            AddHandler title.DataBinding, AddressOf title_DataBinding
            Dim linebreak As New LiteralControl("<br/>")
            Dim caption As New Label
            AddHandler caption.DataBinding, _
                AddressOf caption_DataBinding
            owner.Controls.Add(title)
            owner.Controls.Add(linebreak)
            owner.Controls.Add(caption)
        End Sub

        Sub caption_DataBinding(ByVal sender As Object, _
            ByVal e As EventArgs)
            Dim source As Label = CType(sender, Label)
            Dim container As VacationHome = _
                CType(source.NamingContainer, VacationHome)
            source.Text = container.Caption
        End Sub


        Sub title_DataBinding(ByVal sender As Object, _
            ByVal e As EventArgs)
            Dim source As Label = CType(sender, Label)
            Dim container As VacationHome = _
                CType(source.NamingContainer, VacationHome)
            source.Text = container.Caption
        End Sub
    End Class
#End Region


    Public Class VacationHomeDesigner
        Inherits ControlDesigner

        Public Overrides Sub Initialize(ByVal Component As IComponent)
            MyBase.Initialize(Component)
            SetViewFlags(ViewFlags.TemplateEditing, True)
        End Sub

        Public Overloads Overrides Function GetDesignTimeHtml() As String
            Return "<span>This is design-time HTML</span>"
        End Function

        Public Overrides ReadOnly Property TemplateGroups() As TemplateGroupCollection
            Get
                Dim collection As New 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
            End Get
        End Property
    End Class

End Namespace
// VacationHome.cs
using 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>"),
    ]
    public class VacationHome : CompositeControl
    {
        private ITemplate templateValue;
        private TemplateOwner ownerValue;

        [
        Bindable(true),
        Category("Data"),
        DefaultValue(""),
        Description("Caption")
        ]
        public virtual string 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))
        ]
        public virtual ITemplate Template
        {
            get
            {
                return templateValue;
            }
            set
            {
                templateValue = value;
            }
        }

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

        protected override void CreateChildControls()
        {
            Controls.Clear();
            ownerValue = new TemplateOwner();

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

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

        public override void DataBind()
        {
            CreateChildControls();
            ChildControlsCreated = true;
            base.DataBind();
        }

    }

    [
    ToolboxItem(false)
    ]
    public class TemplateOwner : WebControl
    {
    }

    #region DefaultTemplate
    sealed class 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


   public class VacationHomeDesigner : ControlDesigner
   {

        public override void Initialize(IComponent Component)
        {
            base.Initialize(Component);
            SetViewFlags(ViewFlags.TemplateEditing, true);
        }

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

        public override 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;
            }
        }
    }

}

Descripción del código

Un control con plantilla extiende CompositeControl si se agrega una propiedad del tipo ITemplate y se define el contenedor de nombres para el control. La definición del contenedor de nombres permitirá al desarrollador de páginas utilizar la sintaxis <#%Container%> en la definición de la plantilla. El control con plantilla también define una propiedad de un tipo que se deriva de Control para alojar los controles definidos en la plantilla. También se implementan reemplazos de atributos y miembros concretos para coordinar la propiedad de la plantilla, el control de host y el comportamiento del contenedor de nombres.

En la lista siguiente se resumen los requisitos de implementación principales para un control con plantilla como muestra VacationHome. En la explicación que acompaña a la lista se proporciona información detallada sobre cada uno de los requisitos. El control VacationHome muestra:

  • La derivación de la clase base CompositeControl. Un control con plantilla es una clase especial de control compuesto. También se puede derivar de WebControl, pero CompositeControl agrega la implementación para INamingContainer, lo que permite el uso de la sintaxis <#%Container%>.

  • La implementación de una propiedad del tipo ITemplate y la aplicación a la misma de atributos de metadatos relevantes para definir su persistencia y su contenedor de nombres.

  • La exposición de una propiedad del tipo Control o una clase derivada de Control que sirve para alojar los controles definidos en el elemento de plantilla. Este control se denomina contenedor de plantilla.

  • El reemplazo del método CreateChildControls para crear instancias de los controles con plantilla en la colección Controls del contenedor de plantilla.

  • Opcionalmente, la definición de la plantilla predeterminada que el control utilizará cuando el desarrollador de páginas no especifique ninguna plantilla.

  • Opcionalmente, la definición de una clase de diseñador para el control. La clase de diseñador permite al desarrollador de páginas editar las plantillas en un diseñador visual.

Los atributos aplicados a la propiedad ITemplate son BrowsableAttribute, PersistenceModeAttribute y TemplateContainerAttribute. TemplateContainerAttribute especifica el tipo del control que el analizador de páginas debe utilizar durante la resolución de la variable Container en una expresión como <#%Container.Title%> de una plantilla. El tipo especificado debe implementar INamingContainer y definir las propiedades de datos (en este caso, Caption y Title) para el control. Este tipo puede ser el tipo del propietario de la plantilla o de un control situado más arriba en el árbol de control. En el control VacationHome, el control cuyo tipo se pasa al constructor TemplateContainerAttribute no es el propietario de la plantilla, sino el propio control VacationHome. BrowsableAttribute se establece en false, dado que generalmente las plantillas no se editan en la ventana de edición de propiedades del diseñador visual. PersistenceModeAttribute se establece en InnerProperty, dado que la especificación de la plantilla está escrita como un elemento interno del control.

El control con plantilla debe definir una propiedad del tipo Control que se convierte en el contenedor para los controles creados por la plantilla. En el ejemplo, el control VacationHome define la propiedad Owner, que es del tipo TemplateOwner, que a su vez se deriva de WebControl. La clase TemplateOwner está marcada con ToolboxItem(false) para indicar que la clase TemplateOwner no necesita la compatibilidad del cuadro de herramientas en el diseñador visual. Para obtener más información, vea ToolboxItemAttribute. Se crean instancias de los controles de la plantilla y se agregan a la propiedad Controls del control Owner. Si el control expone varias propiedades ITemplate, podría definir una propiedad de contenedor de plantilla independiente para cada plantilla. La propiedad Owner se expone como propiedad pública. Esto permite al diseñador de páginas utilizar el método FindControl para hacer referencia a controles concretos en la plantilla en tiempo de ejecución.

El control VacationHome reemplaza al método base CreateChildControls. El método CreateChildControls crea instancias de los controles especificados en la propiedad Template y los agrega a la colección Controls del objeto Owner. A continuación, el objeto Owner se agrega a la colección Controls de la instancia de VacationHome, con lo que se podrá representar el control.

Si el desarrollador de páginas no ha definido una plantilla, VacationHome creará una instancia de DefaultTemplate, que se deriva de ITemplate. El método InstantiateIn crea dos controles Label para mostrar las propiedades Title y Caption. Se crea un método controlador de eventos para el evento DataBinding de cada control. El controlador de eventos DataBinding establece la propiedad Text en la propiedad correspondiente (Title o Caption) de VacationHome.

La clase VacationHomeDesigner que implementa un diseñador para la clase VacationHome se deriva de ControlDesigner. Durante la inicialización, el método SetViewFlags, al que se llama con TemplateEditing, permite la edición de la plantilla en tiempo de diseño. El método GetDesignTimeHtml se reemplaza para representar el control cuando no está en modo de edición de plantillas. El código de la propiedad TemplateGroups reemplazada define un grupo de plantillas que contiene una plantilla. Cada objeto TemplateGroup agrega una opción de edición de plantillas a la interfaz de usuario de edición de plantillas del diseñador visual. (En Visual Studio 2005, las opciones de edición de plantillas se muestran en una etiqueta inteligente asociada al control.) En el control VacationHome, la única opción de edición es "Item". Cada objeto TemplateDefinition crea una plantilla para la edición en el diseñador. El parámetro templatePropertyName del constructor TemplateDefinition especifica el nombre de la propiedad de plantilla en el control. DesignerAttribute se aplica a la clase VacationHome para especificar la clase de diseñador.

Página de prueba del control VacationHome

El ejemplo siguiente muestra una página .aspx que utiliza el control VacationHome. La primera instancia del control de la página especifica una plantilla para la propiedad ITemplate del control. La segunda instancia no especifica la propiedad ITemplate, lo que hace que el control VacationHome utilice su plantilla predeterminada en tiempo de ejecución.

<%@ Page Language="VB"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script >
    Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
        If Not IsPostBack Then
            VacationHome1.DataBind()
            VacationHome2.DataBind()
        End If
    End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" >
    <title>
      VacationHome Control Test Page
    </title>
  </head>
  <body>
    <form id="form1" >
    <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"  
        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 >
  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" >
    <title>
      VacationHome Control Test Page
    </title>
  </head>
  <body>
    <form id="form1" >
    <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"  
        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>

Generar y utilizar el ejemplo

Para obtener información sobre la generación del control y su uso en una página, vea Generar ejemplos de controles de servidor personalizados. Debe agregar una referencia al ensamblado System.Design para la compilación.

Vea también

Conceptos

Ejemplo de control Web compuesto

Ejemplo de estilos con establecimiento inflexible de tipos para controles secundarios

Otros recursos

Desarrollar controles de servidor ASP.NET personalizados