共用方式為


樣板化伺服器控制項範例

更新:2007 年 11 月

本範例將示範名稱為 VacationHome 的控制項實作樣板化伺服器控制項的方法。VacationHome 控制項會定義兩個公開 (Expose) 的屬性:Title 和 Caption。網頁設計工具 (Page Designer) 會在設計階段設定這些屬性的值,而控制項會在執行階段使用這些屬性值,為其子控制項設定屬性。藉由編輯控制項中的 <Template> 項目,網頁開發人員可以指定定義控制項之使用者介面的控制項和標記。同樣地,控制項也讓網頁開發人員能夠使用 <#% Container %> 語法,這樣就可以在設計階段於樣板標記中參考 Title 和 Caption 值,並在呈現的輸出中加以顯示。網頁設計工具可以建立看起來像這樣的 ASP.NET Web 網頁:

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

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

}

程式碼討論

樣板化控制項藉由加入型別為 ITemplate 的屬性,並定義控制項的命名容器,即可擴充 CompositeControl。透過定義命名容器,可讓網頁開發人員在樣板定義中使用 <#%Container%> 語法。樣板控制項也會定義其衍生自 Control 的型別屬性,以裝載在樣板中定義的控制項。將實作特定屬性 (Attribute) 和成員覆寫,以協調樣板屬性 (Property)、主控制項和命名容器行為。

下列清單摘要了樣板化控制項的主要實作需求,如 VacationHome 所示範。清單之後的討論會提供每項需求的詳細資料。VacationHome 控制項將示範:

  • 衍生自 CompositeControl 基底類別。樣板化控制項是複合控制項 (Composite Control) 的一種特殊類型。也可以衍生自 WebControl,但 CompositeControl 會加入 INamingContainer 的實作,讓控制項能夠使用 <#%Container%> 語法。

  • 實作 ITemplate 型別的屬性 (Property) 並將相關的中繼資料屬性 (Metadata Attribute) 套用至該屬性,以定義其持續性 (Persistence) 和命名容器。

  • 公開 Control 型別的屬性或衍生自 Control 的類別,做為裝載在樣板項目中定義的控制項。這個控制項也稱為樣板容器。

  • 覆寫 CreateChildControls 方法,在樣板容器的 Controls 集合中產生樣板控制項的實體。

  • 選項性的定義預設樣板,這是網頁開發人員沒有指定樣板時,控制項會使用的樣板。

  • 選項性的,定義 ControlDesigner 類別,ControlDesigner 類別可讓網頁開發人員在視覺化設計工具 (Visual Designer) 中編輯樣板。

套用至 ITemplate 屬性 (Property) 的屬性 (Attribute) 為 BrowsableAttributePersistenceModeAttributeTemplateContainerAttributeTemplateContainerAttribute 會指定在運算式 (例如樣板中的 <#%Container.Title%>) 中解析 Container 變數時,網頁剖析器 (Parser) 應該使用的控制項型別。指定的型別必須對控制項實作 INamingContainer 並定義資料屬性 (在這個例子中是 Caption 和 Title)。這個型別可以是樣板擁有者的型別,或控制項樹狀結構上層控制項的型別。在 VacationHome 控制項中,其型別傳遞至 TemplateContainerAttribute 建構函式的控制項不是樣板擁有者,而是 VacationHome 控制項本身。BrowsableAttribute 會設為 false,因為樣板通常無法在視覺化設計工具的屬性編輯視窗中編輯。PersistenceModeAttribute 會設為 InnerProperty,因為樣板規格會撰寫為控制項的內部項目。

樣板化控制項必須定義型別 Control 的屬性,這個屬性會成為樣板所建立之控制項的容器。在範例中,VacationHome 控制項會定義 Owner 屬性 (型別為 TemplateOwner),這是從 WebControl 衍生而來的屬性。TemplateOwner 類別會以 ToolboxItem(false) 標記,表示 TemplateOwner 類別不需要視覺化設計工具中的工具箱支援。如需詳細資訊,請參閱 ToolboxItemAttribute。樣板中的控制項是可以被建立為控制項實體,並加入到 Owner 控制項的 Controls 屬性。如果控制項公開多個 ITemplate 屬性,您可能要對每個樣板定義不同的樣板容器屬性。Owner 屬性會公開為公用屬性。這可讓網頁設計人員使用 FindControl 方法,在執行階段時參考樣板中的特定控制項。

VacationHome 控制項會覆寫基底 CreateChildControls 方法。CreateChildControls 方法會實體化 Template 屬性中指定的控制項,並將這些控制項加入至 Owner 物件的 Controls 集合。Owner 物件接著會加入至 VacationHome 執行個體的 Controls 集合,然後便可呈現該控制項。

如果網頁設計人員尚未定義樣板,VacationHome 會建立 DefaultTemplate 執行個體 (衍生自 ITemplate)。InstantiateIn 方法會建立兩個 Label 控制項,以顯示 Title 和 Caption 屬性。為每個控制項的 DataBinding 事件,建立事件處理常式方法。DataBinding 事件處理常式會將 Text 屬性設為 VacationHome 的適當屬性 (Title 或 Caption)。

實作 VacationHome 類別設計工具的 VacationHomeDesigner 類別衍生自 ControlDesigner。初始設定期間,使用 TemplateEditing 呼叫的 SetViewFlags 方法會在設計階段時啟用樣板編輯。當控制項不是在樣板編輯模式時,便會覆寫 GetDesignTimeHtml 方法以呈現此控制項。所覆寫的 TemplateGroups 屬性中之程式碼,會定義包含一個樣板的一個樣板群組。每個 TemplateGroup 物件會將樣板編輯選項,加入視覺化設計工具的樣板編輯使用者介面中 (在 Visual Studio 2005 中,樣板編輯選項會顯示在與控制項關聯的智慧標籤中)。在 VacationHome 控制項中,唯一的編輯選項是「項目」(Item)。每個 TemplateDefinition 物件會建立樣板,以在設計工具中進行編輯。TemplateDefinition 建構函式的 templatePropertyName 參數會指定控制項中樣板屬性的名稱。DesignerAttribute 會套用至 VacationHome 類別,以指定設計工具類別。

VacationHome 控制項的測試頁

下列範例顯示使用 VacationHome 控制項的 .aspx 網頁。網頁中控制項的第一個執行個體,會指定控制項的 ITemplate 屬性樣板。第二個執行個體不會指定 ITemplate 屬性,因此會使 VacationHome 控制項在執行階段時使用其預設樣板。

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

建置及使用範例

如需建置控制項以及在網頁中使用此控制項的詳細資訊,請參閱建置自訂伺服器控制項範例。在這個範例應用程式中,您必須要加入一個參考為 System.Design 組件,以便進行編譯。

請參閱

概念

複合 Web 控制項範例

子控制項的具型別樣式範例

其他資源

開發自訂的 ASP.NET 伺服器控制項