Пример шаблонного серверного элемента управления
Обновлен: Ноябрь 2007
В данном примере приводится элемент управления VacationHome, с помощью которого демонстрируется методика реализации шаблонного серверного элемента управления. В элементе управления VacationHome определяются два свойства: Title и Caption. Конструктор страниц задает значения этих свойств во время разработки, а элемент управления использует эти значения во время выполнения для задания свойств своих дочерних элементов управления. При редактировании элемента <Template> в элементе управления конструктор страниц формирует элементы управления и разметку, которые определяют пользовательский интерфейс элемента управления. Элемент управления также позволяет разработчикам страниц использовать синтаксис <#% Container %>, поэтому значения Title и Caption могут использоваться в шаблоне разметки во время разработки и отображаться при отрисовке выходных данных. Конструктор страниц может создать веб-страницу ASP.NET, которая выглядит подобным образом:
<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>
Листинг кода для элемента управления 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;
}
}
}
}
Рассмотрение кода
Шаблонный элемент управления расширяет элемент управления CompositeControl, добавляя свойство типа ITemplate и определяя контейнер именования для элемента управления. Определение контейнера именования позволяет конструктору страниц использовать синтаксис <#%Container%> в определении шаблона. В шаблонном элементе управления также определяется свойство типа, производного от Control, предназначенное для размещения элементов управления, определенных в шаблоне. Для координации свойства шаблона, ведущего элемента управления и поведения контейнера именования реализован ряд атрибутов и переопределений членов.
В приведенном ниже списке сведены общие требования к реализации шаблонного элемента управления на примере VacationHome. Подробные сведения о каждом требовании приведены в обсуждении после списка. Элемент управления VacationHome демонстрирует:
Наследование от базового класса CompositeControl. Шаблонный элемент управления — это особый тип составного элемента управления. Также можно использовать наследование от класса WebControl, но элемент управления CompositeControl добавляет реализацию интерфейса INamingContainer, которая допускает использование синтаксиса <#%Container%>.
Реализацию свойства типа ITemplate и применение к нему релевантных атрибутов метаданных для задания его свойства сохраняемости и контейнера именования.
Предоставление доступа к свойству типа Control или класса, производного от Control, которое служит для размещения элементов управления, определенных в шаблонном элементе. Данный элемент управления называется контейнером шаблона.
Переопределение метода CreateChildControls для создания экземпляров шаблонных элементов управления в коллекции Controls контейнера шаблона.
Определение шаблона по умолчанию (необязательно), который используется элементом управления, если разработчик страницы не указал шаблон.
Определение класса конструктора для элемента управления (необязательно). Класс конструктора позволяет разработчику страницы изменять шаблоны в визуальном конструкторе.
К свойству ITemplate применяются атрибуты BrowsableAttribute, PersistenceModeAttribute и TemplateContainerAttribute. Атрибут TemplateContainerAttribute задает тип элемента управления, который синтаксический анализатор страниц должен использовать при разрешении переменной Container в выражении внутри шаблона — например, <#%Container.Title%>. Указанный тип должен реализовывать интерфейс INamingContainer и определять свойства данных (в данном случае это Caption и Title) для элемента управления. Данный тип может быть типом владельца шаблона или элемента управления, находящегося выше в дереве элементов управления. В случае элемента управления VacationHome в качестве элемента управления, тип которого передается конструктору TemplateContainerAttribute, выступает не владелец шаблона, а сам элемент управления VacationHome. Атрибут BrowsableAttribute получает значение false, так как шаблоны обычно не редактируются в окне редактирования свойств визуального конструктора. Атрибут PersistenceModeAttribute получает значение InnerProperty, так как спецификация шаблона записывается в качестве элемента, внутреннего по отношению к элементу управления.
В шаблонном элементе управления должно определяться свойство типа Control, которое становится контейнером для элементов управления, создаваемых в шаблоне. В примере в элементе управления VacationHome определяется свойство Owner, которое относится к типу TemplateOwner, который, в свою очередь, является производным от WebControl. Класс TemplateOwner помечен атрибутом ToolboxItem(false) для обозначения того, что класс TemplateOwner не требует поддержки панели элементов в визуальном конструкторе. Дополнительные сведения см. в разделе ToolboxItemAttribute. Элементы управления в шаблоне создаются и добавляются в свойство Controls элемента управления Owner. Если элемент управления предоставляет доступ к нескольким свойствам ITemplate, то необходимо задавать отдельное свойство контейнера шаблона для каждого шаблона. К свойству Owner предоставляется общий доступ. Это позволяет конструктору страниц использовать метод FindControl для обращения к отдельным элементам управления в шаблоне во время выполнения.
Элемент управления VacationHome переопределяет базовый метод CreateChildControls. Метод CreateChildControls создает элементы управления, указанные в свойстве Template, и добавляет их в коллекцию Controls объекта Owner. Затем объект Owner добавляется в коллекцию Controls экземпляра VacationHome, после чего элемент управления может быть отрисован.
Если разработчик страницы не задал шаблон, то VacationHome создает экземпляр DefaultTemplate, который является производным от интерфейса ITemplate. Метод InstantiateIn создает два элемента управления Label для отображения свойств Title и Caption. Помимо этого, для события DataBinding каждого элемента управления создается метод-обработчик событий. Обработчик событий DataBinding присваивает свойству Text значение из соответствующего свойства (Title или Caption) элемента управления VacationHome.
Класс VacationHomeDesigner, который реализует визуальный конструктор для элемента управления VacationHome, является производным от ControlDesigner. В ходе инициализации метод SetViewFlags, вызванный с параметром TemplateEditing, разрешает изменение шаблона во время разработки. Метод GetDesignTimeHtml переопределяется, чтобы отрисовывать элемент управления, когда он не находится в режиме изменения шаблона. Код в переопределенном свойстве TemplateGroups определяет одну группу шаблонов, которая содержит один шаблон. Каждый объект TemplateGroup добавляет вариант изменения шаблона к пользовательскому интерфейсу визуального конструктора при изменении шаблонов (в Visual Studio 2005 команды изменения шаблонов отображаются в смарт-теге, связанном с элементом управления). В элементе управления VacationHome единственным вариантом изменения является «Элемент». Каждый объект TemplateDefinition создает шаблон для изменения в конструкторе. Параметр templatePropertyName конструктора TemplateDefinition указывает имя свойства шаблона в элементе управления. Атрибут DesignerAttribute применяется к классу VacationHome для указания класса визуального конструктора.
Тестовая страница для элемента управления VacationHome
В следующем примере показана страница ASPX, использующая элемент управления VacationHome. Первый экземпляр элемента управления на странице задает шаблон для свойства 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 runat="server">
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" 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>
Построение и использование примера
Дополнительные сведения о построении элемента управления и его использовании на странице см. в разделе Примеры связывания пользовательского серверного элемента управления. Для компиляции необходимо добавить ссылку на сборку System.Design.
См. также
Основные понятия
Пример составного веб-элемента управления
Типизированные стили для дочерних элементов управления
Другие ресурсы
Разработка пользовательских серверных элементов управления ASP.NET