Beispiel für ein vorlagenbasiertes Serversteuerelement

Aktualisiert: November 2007

Dieses Beispiel zeigt ein Steuerelement mit dem Namen VacationHome, das die Implementierung eines vorlagenbasierten Serversteuerelements veranschaulicht. Das VacationHome-Steuerelement definiert zwei verfügbar gemachte Eigenschaften: Title und Caption. Die Werte dieser Eigenschaften werden zur Entwurfszeit vom Seitenentwickler festgelegt und vom Steuerelement verwendet, um Eigenschaften für dessen untergeordnete Steuerelemente zu bestimmen. Durch Bearbeiten des <Template>-Elements im Steuerelement gibt der Seitenentwickler die Steuerelemente und das Markup zur Definition der Benutzeroberfläche des Steuerelements an. Des Weiteren ermöglicht das Steuerelement es dem Seitenentwickler, die <#% Container %>-Syntax zu verwenden, sodass zur Entwurfszeit auf Title- und Caption-Werte im Vorlagenmarkup verwiesen werden kann und diese in der wiedergegebenen Ausgabe angezeigt werden können. Die von einem Seitendesigner erstellte ASP.NET-Webseite könnte folgendermaßen aussehen:

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

Codeauflistung für das VacationHome-Steuerelement

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

}

Codeerläuterung

Ein vorlagenbasiertes Steuerelement erweitert CompositeControl durch Hinzufügen einer Eigenschaft vom Typ ITemplate und durch Definieren eines Namenscontainers für das Steuerelement. Durch die Definition des Namenscontainers ermöglichen Sie dem Seitenentwickler, in der Vorlagendefinition die <#%Container%>-Syntax zu verwenden. Um die in der Vorlage definierten Steuerelemente zu hosten, definiert das Vorlagensteuerelement außerdem einen Eigenschaftentyp, der von Control abgeleitet wird. Bestimmte Attribute und Memberüberschreibungen werden implementiert, um das Verhalten von Vorlageneigenschaft, Hoststeuerelement und Namenscontainer zu koordinieren.

Die folgende Liste fasst die wesentlichen Implementierungsanforderungen für ein vorlagenbasiertes Steuerelement anhand des Beispiels von VacationHome zusammen. Eine genaue Erörterung der einzelnen Anforderungen erfolgt im Anschluss an diese Liste. Folgende Punkte werden am Beispiel des VacationHome-Steuerelements veranschaulicht:

  • Ableiten von der CompositeControl-Basisklasse. Ein vorlagenbasiertes Steuerelement stellt eine spezielle Form eines zusammengesetzten Steuerelements dar. Zwar können Sie auch von WebControl ableiten, jedoch hat CompositeControl den Vorteil der Implementierung von INamingContainer. Dadurch lässt sich die <#%Container%>-Syntax verwenden.

  • Implementieren einer Eigenschaft vom Typ ITemplate und Anwenden wichtiger Metadatenattribute auf diese Eigenschaft, um die Beibehaltung und den Namenscontainer dieser Eigenschaft zu definieren.

  • Verfügbarmachen einer Eigenschaft vom Typ Control oder einer von Control abgeleiteten Klasse, die dem Hosten der im Vorlagenelement definierten Steuerelemente dient. Dieses Steuerelement wird als Vorlagencontainer bezeichnet.

  • Überschreiben der CreateChildControls-Methode zum Instanziieren der Vorlagensteuerelemente in der Controls-Auflistung des Vorlagencontainers.

  • Definieren einer vom Steuerelement verwendeten Standardvorlage, wenn der Seitenentwickler keine Vorlage angibt (optional).

  • Definieren einer Designerklasse für das Steuerelement (optional). Mit der Designerklasse kann der Seitenentwickler die Vorlagen in einem visuellen Designer bearbeiten.

Die auf die ITemplate-Eigenschaft angewendeten Attribute sind BrowsableAttribute, PersistenceModeAttribute und TemplateContainerAttribute. Das TemplateContainerAttribute gibt den Typ des Steuerelements an, den der Seitenparser in einer Vorlage beim Auflösen der Container-Variable in einem Ausdruck wie <#%Container.Title%> verwenden soll. Der angegebene Typ muss INamingContainer implementieren und die Dateneigenschaften (in diesem Fall Caption und Title) für das Steuerelement definieren. Dieser Typ kann der Typ des Vorlagenbesitzers sein oder der Typ eines in der Steuerelementstruktur weiter oben stehenden Steuerelements. Im VacationHome-Steuerelement handelt es sich bei dem an den TemplateContainerAttribute-Konstruktor übergebenen Steuerelementtyp nicht um den Vorlagenbesitzer, sondern um das VacationHome-Steuerelement selbst. Das BrowsableAttribute wird auf false festgelegt, da Vorlagen im Eigenschaftenbearbeitungsfenster eines visuellen Designers in der Regel nicht bearbeitet werden. Das PersistenceModeAttribute wird auf InnerProperty festgelegt, da die Vorlagenspezifikation als inneres Element des Steuerelements geschrieben wird.

Das vorlagenbasierte Steuerelement muss eine Eigenschaft vom Typ Control definieren, die zum Container für die von der Vorlage erstellten Steuerelemente wird. Im Beispiel definiert das VacationHome-Steuerelement die Owner-Eigenschaft vom Typ TemplateOwner, welche wiederum von WebControl abgeleitet wird. Die TemplateOwner-Klasse ist als ToolboxItem(false) gekennzeichnet und zeigt damit an, dass die TemplateOwner-Klasse in einem visuellen Designer keine Toolbox-Unterstützung benötigt. Weitere Informationen finden Sie unter ToolboxItemAttribute. Die Steuerelemente in der Vorlage werden instanziiert und der Controls-Eigenschaft des Owner-Steuerelements hinzugefügt. Wenn das Steuerelement mehrere ITemplate-Eigenschaften verfügbar macht, können Sie für jede Vorlage eine eigene Vorlagencontainereigenschaft definieren. Die Owner-Eigenschaft wird als öffentliche Eigenschaft verfügbar gemacht. Dadurch kann der Seitendesigner die FindControl-Methode verwenden, um zur Laufzeit auf bestimmte Steuerelemente in der Vorlage zu verweisen.

Das VacationHome-Steuerelement überschreibt die CreateChildControls-Basismethode. Die CreateChildControls-Methode instanziiert die in der Template-Eigenschaft angegebenen Steuerelemente und fügt sie der Controls-Auflistung des Owner-Objekts hinzu. Der Controls-Auflistung der VacationHome -Instanz wird anschließend das Owner-Objekt hinzugefügt, und das Steuerelement kann dargestellt werden.

Wenn der Seitenentwickler keine Vorlage definiert hat, erstellt VacationHome eine Instanz von DefaultTemplate, die von ITemplate abgeleitet wird. Die InstantiateIn-Methode erstellt zwei Label-Steuerelemente, um die Title-Eigenschaft und die Caption-Eigenschaft anzuzeigen. Für das DataBinding-Ereignis jedes Steuerelements wird eine Ereignishandlermethode erstellt. Der DataBinding-Ereignishandler legt die Text-Eigenschaft auf die entsprechende Eigenschaft (Title oder Caption) von VacationHome fest.

Die VacationHomeDesigner-Klasse, die für die VacationHome-Klasse einen Designer implementiert, wird von ControlDesigner abgeleitet. Während der Initialisierung ermöglicht die mit TemplateEditing aufgerufene SetViewFlags-Methode das Bearbeiten einer Vorlage zur Entwurfszeit. Die GetDesignTimeHtml-Methode wird überschrieben, um das Steuerelement darzustellen, wenn es sich nicht im Vorlagenbearbeitungsmodus befindet. Im Code der überschriebenen TemplateGroups-Eigenschaft wird eine Vorlagengruppe definiert, die eine einzige Vorlage enthält. Jedes TemplateGroup-Objekt fügt der Benutzeroberfläche des visuellen Designers zur Vorlagenbearbeitung eine Vorlagenbearbeitungsoption hinzu. (In Visual Studio 2005 werden die Vorlagenbearbeitungsoptionen in einem dem Steuerelement zugeordneten Smarttag angezeigt.) Im VacationHome-Steuerelement ist "Item" die einzige Bearbeitungsauswahl. Jedes TemplateDefinition-Objekt erstellt eine Bearbeitungsvorlage für den Designer. Der templatePropertyName-Parameter des TemplateDefinition-Konstruktors gibt den Namen der Vorlageneigenschaft im Steuerelement an. Das DesignerAttribute wird auf die VacationHome-Klasse angewendet, um die Designerklasse anzugeben.

Testseite für das VacationHome-Steuerelement

Das folgende Beispiel zeigt eine ASPX-Seite, die das VacationHome-Steuerelement verwendet. Die erste Instanz des Steuerelements in der Seite gibt eine Vorlage für die ITemplate-Eigenschaft des Steuerelements an. Da für die zweite Instanz keine ITemplate-Eigenschaft angegeben wird, verwendet das VacationHome-Steuerelement zur Laufzeit die Standardvorlage.

<%@ 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>

Erstellen und Verwenden des Beispiels

Informationen zum Erstellen eines Steuerelements und über dessen Verwendung in einer Seite finden Sie unter Erstellen der Beispiele für benutzerdefinierte Serversteuerelemente. Für die Kompilierung müssen Sie einen Verweis auf die System.Design-Assembly hinzufügen.

Siehe auch

Konzepte

Beispiel für ein zusammengesetztes Steuerelement

Beispiel für typisierte Formate für untergeordnete Steuerelemente

Weitere Ressourcen

Entwickeln von benutzerdefinierten ASP.NET-Serversteuerelementen