更新:2007 年 11 月
本演练演示如何创建公开可绑定数据源的简单的数据绑定服务器控件。
ASP.NET 数据绑定服务器控件为代表记录集合或项集合的数据源提供一个用户界面 (UI)。DataList 和 Repeater 服务器控件是数据绑定服务器控件的示例。有关 ASP.NET 附带的数据绑定控件的更多信息,请参见 ASP.NET 数据绑定 Web 服务器控件概述。
![]() |
---|
本演练中演示的控件与 ASP.NET 版本 1.1 兼容。有关为 ASP.NET 2.0 创建自定义数据绑定 Web 控件的更多信息,请参见为 ASP.NET 2.0 开发自定义数据绑定 Web 服务器控件。 |
ASP.NET 1.1 数据绑定服务器控件可以绑定到下列任何类型:
IEnumerable(如 Array、ArrayList 或 Hashtable 对象)。
IListSource(如 DataSet 对象)。
在本演练中,您将创建可以绑定到实现 IEnumerable 接口的任何对象的数据绑定控件。
本演练涉及以下任务:
创建网站以测试自定义数据绑定控件。
创建数据绑定控件类。该类必须公开 IListSource 或 IEnumerable 类型的属性,页开发人员才能指定要绑定的数据的位置。它还必须重写基类的 DataBind 和 CreateChildControls 方法。
在 Web.config 文件中注册控件。
在 ASP.NET 网页中测试控件。
编译控件以便将其作为二进制代码分发。
测试编译的自定义数据绑定服务器控件。
创建网站以测试控件
可以使用 ASP.NET 动态编译在页中测试控件,而无需将控件编译为程序集。ASP.NET 动态编译放置在网站根目录的 App_Code 目录中的代码。这样就可以从页访问 App_Code 中的源文件中的类,而无需将其手动编译为程序集。
![]() |
---|
App_Code 目录是 ASP.NET 1.0 和 1.1 中都没有的新功能。使用 App_Code 目录进行初始控件测试是一个可选步骤。生成服务器控件的主要步骤与早期版本相同,如“将控件编译为程序集”一节所示。 |
创建网站以测试自定义数据绑定控件
创建一个名为 ServerControlsTest 的网站。
可以在 IIS 中将该站点创建为名为 ServerControlsTest 的虚拟目录。有关创建和配置 IIS 虚拟目录的详细信息,请参见 如何:在 IIS 5.0 和 6.0 中创建和配置虚拟目录。
直接在网站的根目录下创建 App_Code 目录。
创建 SimpleDataBoundControl 类
下一步为创建控件类。
创建 SimpleDataBoundControl 类
在前面的步骤中创建的 App_Code 文件夹中,创建名为 SimpleDataBoundControl.cs 或 SimpleDataBoundControl.vb 的类,具体取决于要使用的编程语言。
向类文件中添加下面的代码。
Imports System Imports System.Collections Imports System.ComponentModel Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace Samples.ASPNet.VB.Controls <DefaultProperty("DataSource")> _ Public Class SimpleDataBoundControl Inherits WebControl Private _dataSource As IEnumerable Private _label As Label Private _button As Button Private _table As Table <Category("Data"), DefaultValue(""), Description("An exposed data source: A public member of type IEnumerable to bind to such as an Array, ArrayList or Hashtable.")> _ Public Overridable Property DataSource() As IEnumerable Get Return _dataSource End Get Set(ByVal value As IEnumerable) If TypeOf value Is IEnumerable OrElse value Is Nothing Then _dataSource = value Else Throw New ArgumentException() End If End Set End Property Protected Overridable Function GetDataSource() As IEnumerable If _dataSource Is Nothing Then Return Nothing End If Dim resolvedDataSource As IEnumerable resolvedDataSource = _dataSource Return resolvedDataSource End Function 'GetDataSource Protected Overridable Sub CreateMyControlHeirarchy(ByVal useViewState As Boolean) Dim resolvedDataSource As IEnumerable = Nothing If useViewState Then If Not (ViewState("RowCount") Is Nothing) Then resolvedDataSource = New Object(Fix(ViewState("RowCount"))) {} Else Throw New Exception("Unable to retrieve expected data from ViewState") End If Else resolvedDataSource = GetDataSource() End If If Not (resolvedDataSource Is Nothing) Then ' Create a label that will indicate form which source the data has been provided. Dim s As String If useViewState Then s = "Data collection retrieved from ViewState:" Else s = "Data collection retrieved from bound data source:" End If _label = New Label() Me.Controls.Add(Me._label) _label.Text = s _button = New Button() Me.Controls.Add(Me._button) _button.Text = "Test re-binding of ViewState" _table = New Table() Me.Controls.Add(Me._table) Dim dataItem As Object For Each dataItem In resolvedDataSource Dim row As New TableRow() _table.Rows.Add(row) Dim cell As New TableCell() If Not useViewState Then cell.Text = dataItem.ToString() End If row.Cells.Add(cell) Next dataItem ViewState("RowCount") = _table.Rows.Count End If End Sub 'CreateMyControlHeirarchy Protected Overrides Sub CreateChildControls() Controls.Clear() If Not (ViewState("RowCount") Is Nothing) Then Dim useViewState As Boolean = True CreateMyControlHeirarchy(useViewState) End If End Sub 'CreateChildControls Public Overrides Sub DataBind() MyBase.OnDataBinding(EventArgs.Empty) Controls.Clear() ClearChildViewState() TrackViewState() Dim useViewState As Boolean = False CreateMyControlHeirarchy(useViewState) ChildControlsCreated = True End Sub 'DataBind Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) Me._label.RenderControl(writer) Me._table.RenderControl(writer) Me._button.RenderControl(writer) End Sub 'RenderContents End Class 'SimpleDataBoundControl End Namespace
using System; using System.Collections; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace Samples.AspNet.CS.Controls { [ DefaultProperty("DataSource") ] public class SimpleDataBoundControl : WebControl { private IEnumerable _dataSource; private Label _label; private Button _button; private Table _table; [ Category("Data"), DefaultValue(""), Description("An exposed data source: A public member of type IEnumerable to bind to such as an Array, ArrayList or Hashtable.") ] public virtual IEnumerable DataSource { get { return _dataSource; } set { if ((value is IEnumerable) || (value == null)) { _dataSource = value; } else { throw new ArgumentException(); } } } protected virtual IEnumerable GetDataSource() { if (_dataSource == null) { return null; } IEnumerable resolvedDataSource; resolvedDataSource = _dataSource as IEnumerable; return resolvedDataSource; } protected virtual void CreateMyControlHeirarchy(bool useViewState) { IEnumerable resolvedDataSource = null; if (useViewState) { if (ViewState["RowCount"] != null) { resolvedDataSource = new object[(int)ViewState["RowCount"]]; } else { throw new Exception("Unable to retrieve expected data from ViewState"); } } else { resolvedDataSource = GetDataSource(); } if (resolvedDataSource != null) { // Create a label that will indicate form which source the data has been provided. String s; if (useViewState) { s = "Data collection retrieved from ViewState:"; } else { s = "Data collection retrieved from bound data source:"; } _label = new Label(); this.Controls.Add(this._label); _label.Text = s; _button = new Button(); this.Controls.Add(this._button); _button.Text = "Test re-binding of ViewState"; _table = new Table(); this.Controls.Add(this._table); foreach (object dataItem in resolvedDataSource) { TableRow row = new TableRow(); _table.Rows.Add(row); TableCell cell = new TableCell(); if (!useViewState) { cell.Text = dataItem.ToString(); } row.Cells.Add(cell); } ViewState["RowCount"] = _table.Rows.Count; } } protected override void CreateChildControls() { Controls.Clear(); if (ViewState["RowCount"] != null) { bool useViewState = true; CreateMyControlHeirarchy(useViewState); } } public override void DataBind() { base.OnDataBinding(EventArgs.Empty); Controls.Clear(); ClearChildViewState(); TrackViewState(); bool useViewState = false; CreateMyControlHeirarchy(useViewState); ChildControlsCreated = true; } protected override void RenderContents(HtmlTextWriter writer) { this._label.RenderControl(writer); this._table.RenderControl(writer); this._button.RenderControl(writer); } } }
代码讨论
SimpleDataBoundControl 类根据其 DataSource 属性中指定的数据集合呈现 HTML 表。ASP.NET 1.1 数据绑定 Web 服务器控件必须公开可以设置为 IEnumerable 或 IListSource 类型的可绑定 DataSource 属性。
在该示例代码中,DataSource 属性的 set 访问器验证要设置的值是 null 还是 IEnumerable 类型的对象。因此,在本示例中,页开发人员可以将控件绑定到任何 IEnumerable 类型,如 Array、ArrayList 或 Hashtable 对象。开发人员最初可以保留将 DataSource 设置为其默认 null 值的状态,并在代码中设置属性。
元数据属性 Category、DefaultValue 和 Description 提供设计工具、ASP.NET 页分析器和公共语言运行库使用的信息。
CreateMyControlHeirarchy 帮助器方法将控件的子控件层次结构的创建保存在一个公共代码路径中。该方法将被重写的 DataBind 方法和重写的 CreateChildControls 方法调用。需要使用重写的 DataBind 方法。该方法枚举关联的数据源中的对象并创建子控件。需要使用重写的 CreateChildControls 方法并根据视图状态中保存的数据重新创建子控件层次结构。
控件通过重写继承的 RenderContents 方法进行呈现。这使得控件能够在其自己的标记中进行呈现。传入 RenderContents 方法的参数是 HtmlTextWriter 类型的对象,是具有呈现标记和其他 HTML(和 HTML 变量)标记的方法的实用工具类。
有关数据绑定 Web 服务器控件所需实现的更多信息,请参见为 ASP.NET 1.1 开发自定义数据绑定 Web 服务器控件。
创建标记前缀
标记前缀是指在页中以声明方式创建控件时出现在控件类型名称前面的前缀,如 <asp:Table /> 中的“asp”。若要在页中以声明方式使用您的控件,则 ASP.NET 需要一个映射到该控件的命名空间的标记前缀。通过在每个使用自定义控件的页上添加一个 @ Register 指令,页开发人员可提供标记前缀/命名空间映射,如以下示例所示:
<%@ Register TagPrefix="aspSample"
Namespace="Samples.AspNet.CS.Controls"%>
<%@ Register TagPrefix="aspSample"
Namespace="Samples.AspNet.VB.Controls"%>
![]() |
---|
ASP.NET 2.0 中的 @ Register 指令与 ASP.NET 1.0 和 ASP.NET 1.1 中的相同。如果您熟悉 ASP.NET 早期版本中的 @ Register 指令,则会注意到前面的 @ Register 指令中缺少指定控件程序集名称的 assembly 属性。如果缺少 assembly 属性,则 ASP.NET 会推断该程序集是从 App_Code 目录中的源文件动态编译而来。 |
除了在每个 .aspx 页中使用 @ Register 指令,页开发人员还可以在 Web.config 文件中指定标记前缀和命名空间映射。如果将在 Web 应用程序的多个页中使用自定义控件,则该方法非常有用。
![]() |
---|
标记前缀的配置项是 ASP.NET 2.0 的新功能。在 ASP.NET 1.0 和 1.1 中,标记前缀映射在每个使用自定义控件的页的 @ Register 指令中指定。 |
下面的过程描述如何在 Web.config 文件中指定标记前缀映射。
在 Web.config 文件中添加标记前缀映射
如果网站还没有一个名为 Web.config 的文件,则请在网站的根目录中创建此文件。
创建了新的(空的)Web.config 文件之后,将以下 XML 标记复制到该文件中并保存该文件。如果您的站点已具有 Web.config 文件,则将以下突出显示的元素添加到该文件中。
说明:
标记前缀项必须是 <controls> 元素的子项,而该元素必须位于 <system.web> 下的 <pages> 节下。
<?xml version="1.0"?> <configuration> <system.web> <pages> <controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.CS.Controls"> </add> </controls> </pages> </system.web> </configuration>
<?xml version="1.0"?> <configuration> <system.web> <pages> <controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.VB.Controls"> </add> </controls> </pages> </system.web> </configuration>
突出显示部分是一个标记前缀项,该项将标记前缀“aspSample”映射到命名空间 Samples.AspNet.CS.Controls 或 Samples.AspNet.VB.Controls。
在配置文件中指定了标记前缀映射之后,可以在网站的任何页中以声明方式使用 SimpleDataBoundControl 控件(如 <aspSample:SimpleDataBoundControl />)。
创建页以使用自定义数据绑定控件
在此部分的演练中,您将创建使您能够测试自定义数据绑定控件的页标记。
创建使用自定义数据绑定控件的页
在网站中创建名为 TestSimpleDataBoundControl.aspx 的文件。
将以下标记复制到 TestSimpleDataBoundControl.aspx 文件中并保存该文件。
<%@ Page Language="VB" %> <%@ Register TagPrefix="aspSample" Namespace="Samples.AspNet.VB.Controls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) If Not IsPostBack Then Dim a As New ArrayList() a.Add("One") a.Add("Two") a.Add("Three") a.Add("Four") simpleDataBoundControl1.DataSource = a simpleDataBoundControl1.DataBind() End If End Sub 'Page_Load </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>SimpleDataBoundControl test page</title> </head> <body> <form id="form1" runat="server"> <div> <aspSample:SimpleDataBoundControl runat="server" id="simpleDataBoundControl1" BorderStyle="Solid" ></aspSample:SimpleDataBoundControl> </div> </form> </body> </html>
<%@ Page Language="C#" Trace="true" EnableViewState="true" %> <%@ Register TagPrefix="aspSample" Namespace="Samples.AspNet.CS.Controls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { ArrayList a = new ArrayList(); a.Add("One"); a.Add("Two"); a.Add("Three"); a.Add("Four"); simpleDataBoundControl1.DataSource = a; simpleDataBoundControl1.DataBind(); } } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>SimpleDataBoundControl test page</title> </head> <body> <form id="form1" runat="server"> <div> <aspSample:SimpleDataBoundControl runat="server" id="simpleDataBoundControl1" BorderStyle="Solid" ></aspSample:SimpleDataBoundControl> </div> </form> </body> </html>
运行 TestSimpleDataBoundControl.aspx 页。
对自定义控件的源代码做一些更改。例如,通过在 RenderContents 方法末尾处添加此行代码来多写一个字符串:
writer.Write("<br />Testing how the App_Code directory works.");
writer.Write("<br />Testing how the App_Code directory works.")
在浏览器中刷新 TestSimpleDataBoundControl.aspx 页。
将看到,尽管没有编译控件,对控件所做的更改仍在页中反映出来了。
将控件编译为程序集
尽管利用 App_Code 目录可以不编译就对控件进行测试,但是如果您希望将您的控件作为对象代码分发给其他开发人员,则必须对其进行编译。此外,如果没有将控件编译为程序集,就不能将该控件添加到可视化设计器的工具箱中。
将控件编译为程序集
按照以下这些步骤设置计算机的 Windows 环境 PATH 变量,使其包含 .NET Framework 的安装路径:
在 Windows 中,右击**“我的电脑”,选择“属性”,单击“高级”选项卡,然后单击“环境变量”**按钮。
在**“系统变量”**列表中,双击 Path 变量。
在**“变量值”**文本框中,将一个分号 (;) 添加到文本框中的现有值的末尾,然后键入 .NET Framework 的安装路径。.NET Framework 通常安装在位于 \Microsoft.NET\Framework\版本号 的 Windows 安装目录中。
单击**“确定”**关闭每个对话框。
从包含在本演练的先前步骤中创建的源文件的 App_Code 目录运行以下命令。
此操作将在 App_Code 目录中生成名为 Samples.AspNet.CS.Controls.dll 或 Samples.AspNet.VB.Controls.dll 的程序集。
csc /t:library /out:Samples.AspNet.CS.Controls.dll /r:System.dll /r:System.Web.dll *.cs
vbc /t:library /out:Samples.AspNet.VB.Controls.dll /r:System.dll /r:System.Web.dll *.vb
/t:library 编译器选项告知编译器创建一个库,而不是创建一个可执行程序集。/out 选项为程序集提供名称,而 /r 选项则列出链接到您的程序集的那些程序集。
说明:
为保持示例独立,本演练要求创建具有单个控件的程序集。通常,.NET Framework 设计指导原则建议不要创建只包含很少的类的程序集。为了便于部署,应尽可能少地创建程序集。
使用 TagPrefixAttribute 提供标记前缀/命名空间映射
在前面的内容中,您已了解页开发人员如何在页或 Web.config 文件中指定标记前缀。当编译控件时,可以选择包含程序集级别的 System.Web.UI.TagPrefixAttribute 属性,该属性建议可视化设计器应为控件使用的默认标记前缀。如果设计器未找到在 Web.config 文件或页中的 @ Register 指令中映射的标记前缀,TagPrefixAttribute 属性就会为可视化设计器提供可以使用的标记前缀,因此该属性非常有用。在工具箱中首次双击控件或将其从工具箱拖动到页中时,此标记前缀将向页进行注册。
如果决定使用 TagPrefixAttribute 属性,则可以在与控件一起编译的单独文件中指定该属性。根据约定,该文件名为 AssemblyInfo.语言扩展,如 AssemblyInfo.cs 或 AssembyInfo.vb。下面的过程介绍如何指定 TagPrefixAttribute 元数据。
![]() |
---|
如果没有在控件的程序集中指定 TagPrefixAttribute,并且页开发人员没有在页或 Web.config 文件中指定标记前缀/命名空间映射,则可视化设计器将创建一个默认的标记前缀。例如,当从工具箱中拖动您的控件时,Visual Studio 2005 将为您的控件创建其自己的控件标记,如 cc1。 |
使用 TagPrefixAttribute 添加标记前缀映射
在源代码目录中创建一个名为 AssemblyInfo.cs 或 AssemblyInfo.vb 的文件并向该文件添加以下代码。
using System; using System.Web.UI; [assembly: TagPrefix("Samples.AspNet.CS.Controls", "aspSample")]
Imports System Imports System.Web.UI <Assembly: TagPrefix("Samples.AspNet.VB.Controls", "aspSample")>
标记前缀属性在命名空间 Samples.AspNet.CS.Controls 或 Samples.AspNet.VB.Controls 与前缀 aspSample 之间创建映射。
使用前面使用的编译命令重新编译所有的源文件(使用或不使用嵌入资源)。
在 ASP.NET 页中使用已编译的自定义数据绑定服务器控件
若要测试自定义控件的已编译版本,则必须使网站中的页可以访问此控件的程序集。
使网站可以访问控件的程序集
如果站点还没有一个 Bin 目录,则请在网站的根目录下创建该目录。
将控件程序集(Samples.AspNet.CS.Controls.dll 或 Samples.AspNet.VB.Controls.dll)从 App_Code 目录拖(移动)到 Bin 目录中。
将控件的源文件从 App_Code 目录移动到网站中的另一个目录中。
如果没有将源文件从 App_Code 目录中移出,则您的控件的类型将同时存在于编译的程序集中和由 ASP.NET 创建的动态生成的程序集中。这在加载该控件时会造成不明确引用,加载任何使用该控件的页时将生成编译器错误。
本演练中创建的程序集必须放在 ASP.NET 网站的 Bin 目录中才能使网站中的页使用您的控件,因此该程序集为私有程序集。如果其他应用程序没有安装该程序集的副本,则这些应用程序都不能访问该程序集。如果要为共享的 Web 宿主应用程序创建控件,通常会将控件打包到一个私有程序集中。但是,如果要创建在专用宿主环境中使用的控件,或要创建 ISP 向所有客户提供的一系列控件,则需要将您的控件打包到安装在全局程序集缓存中的一个共享(强名称)程序集中。有关更多信息,请参见使用程序集和全局程序集缓存。
接下来,必须修改在 Web.config 文件中创建的标记前缀映射,以指定控件的程序集名称。
在 Web.config 文件中修改标记前缀映射
编辑 Web.config 文件以将 assembly 属性添加到 <add tagPrefix=name> 元素:
<controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.CS.Controls" assembly="Samples.AspNet.CS.Controls"> </add> </controls>
<controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.VB.Controls" assembly="Samples.AspNet.VB.Controls"> </add> </controls>
assembly 属性指定控件所在的程序集的名称。<add tagPrefix=name> 元素将标记前缀映射到命名空间/程序集组合。ASP.NET 从 App_Code 目录中的源文件动态生成程序集时,无需提供程序集属性。未使用程序集属性时,ASP.NET 会从通过 App_Code 目录动态生成的程序集加载控件的类型。
现在您便可以通过在页中查看控件来对其进行测试。
查看使用自定义控件的页
通过在地址栏中输入以下 URL 在浏览器中显示 TestSimpleDataBoundControl.aspx 页:
https://localhost/ServerControlsTest/TestSimpleDataBoundControl.aspx
如果您在可视化设计器(如 Visual Studio 2005)中使用您的控件,则您可以将您的控件添加到工具箱中,可将该控件从工具箱拖到设计图面,还可在属性浏览器中访问该控件的属性和事件。此外,在 Visual Studio 2005 中,您的控件在网页设计器的“源”视图和代码编辑器中具有完整的 IntelliSense 支持。
后续步骤
该简单的自定义数据绑定服务器控件演示用于创建自定义控件的基本步骤,此自定义控件能够为页开发人员提供将其绑定到外部数据源的标准方法。您可以从此处着手研究 Visual Studio 如何帮助您创建复杂的自定义服务器控件。建议研究的方面包括:
查看对开发自定义数据绑定服务器控件的通用结构的介绍。有关详细信息,请参见为 ASP.NET 1.1 开发自定义数据绑定 Web 服务器控件。
调查您可以扩展和自定义的现有数据绑定服务器控件。有关可用的数据绑定 Web 服务器控件的概述,请参见 ASP.NET 数据绑定 Web 服务器控件概述。有关数据绑定 Web 服务器控件类的信息,请参见下列参考主题:GridView、DetailsView、FormView、Repeater 和 DataList。
了解有关呈现、定义属性、保持状态和实现自定义复合控件的更多信息。有关详细信息,请参见开发自定义 ASP.NET 服务器控件、控件状态与视图状态示例、服务器控件自定义状态管理。
了解可以应用于自定义数据绑定服务器控件及其成员以提供设计工具所用信息的元数据属性。有关详细信息,请参见自定义服务器控件的元数据属性。
创建用于添加设计时功能、模板或样式的控件设计器。有关自定义数据绑定控件设计器的示例,请参见 HierarchicalDataBoundControlDesigner 和演练:为 Web 服务器控件创建基本控件设计器。有关 ASP.NET 控件设计器的概述,请参见 ASP.NET 控件设计器概述。
使用自定义数据绑定控件设计器的示例。有关详细信息,请参见 HierarchicalDataBoundControlDesigner 主题。
研究有关定义合适的设计时属性以使自定义服务器控件在 Visual Studio 工具箱中可用的演示。有关详细信息,请参见如何:在 Visual Studio 中使用自定义 ASP.NET 服务器控件。
请参见
任务
概念
参考
HierarchicalDataBoundControlDesigner