对于本专栏,我假设您已经对 Web 部件的基本知识有所了解,例如,如何使用 WebPartManager 控件、Web 部件区域、编辑器、目录和持久性属性。如果您还不了解,建议您在继续阅读本文之前先阅读刚才提到的那篇文章。
创建用于 ASP.NET 2.0 应用程序的 Web 部件
您可以用两种方法创建 Web 部件。第一种方法涉及创建一个自定义的 Web 部件类,该类从 System.Web.UI.WebControls.WebParts 命名空间中定义的 WebPart 类继承。使用该方法时,将自定义的 Web 部件类打包到一个程序集 DLL 中通常是有意义的,因为这样可以提供对重用、版本控制和 Visual Studio® 2005 集成的更多控制。如果您对使用以前的 ASP.NET 版本生成自定义控件很熟悉,则许多相同的技术适用于将自定义的 Web 部件生成到 DLL 程序集中。
用于创建 ASP.NET 2.0 Web 部件的第二种方法涉及使用用户控件。虽然该方法不产生相同的重用和版本控制级别,但是它的确允许您使用 Visual Studio 窗体设计器来创建 Web 部件的用户界面部分。如果您想通过将用于用户输入、验证和数据绑定的控件拖放到设计界面上来创建应用程序,则该方法适合于您。当然,如果您已经花时间创建了一个您想用作 Web 部件的用户控件,它也是个可以采用的好方法。
当创建一个专门设计为 Web 部件的用户控件时,建议您实现 IWebPart 接口。这样,Web 部件后台的代码就可通过编程方式分配自己的几个内部 Web 部件属性,如它的 Title 和 TitleIconUrl。
本月专栏附带的代码示例使用一个名为 WebPartBase 的自定义基类,该基类从 UserControl 继承并实现 IwebPart。该基类的定义部署在 App_Code 目录中名为 WebPartBase.vb 的源文件中。每当您使用用户控件创建一个新 Web 部件时,只需在该代码隐藏文件中更改该基类以利用该技术:
Partial Class WebParts_Customers
Inherits WebPartBase
Sub New()
Title = "Northwind Customer List"
TitleIconImageUrl = "~\img\Customers.gif"
End Sub
End Class
设计可连接的 Web 部件
使用 Web 部件连接,您可以使用户更轻松地形象化数据各项之间存在的关系。例如,Web 部件连接可以建模一个主-从方案,其中显示客户列表的 Web 部件连接到另一个显示当前所选客户详细信息的 Web 部件。图 1 的示例说明这种设计可能生成的用户界面外观。
Web 部件连接还可用于建模一对多关系。例如,显示客户列表的 Web 部件可以连接到另一个显示针对当前所选客户的所有定单的 Web 部件。
通常使用 Web 部件连接建模的另一个方案是表单查询。在这种方案中,一个 Web 部件提供一个用户界面,该用户界面允许用户选择查询数据(如数据库表)时所用的搜索或筛选条件。然后,该 Web 部件连接到另一个显示查询结果的 Web 部件。Web 部件连接用于在运行查询前,将筛选条件从一个 Web 部件传递到另一个 Web 部件。
Web 部件连接基于提供者和使用者的概念。提供者 Web 部件通过一个编程接口为一个或多个使用者 Web 部件提供信息。提供者和使用者之间交换的信息可以是简单的数据项(如数字或字符串),也可以是较特殊的内容(如对一个复杂数组或自定义对象集合的引用)。
如果针对 Windows® SharePoint® Services 2.0 (WSS) 编写了 Web 部件,您可能已经熟悉它用于连接 Web 部件的模型。在 WSS 中,Web 部件只能使用一组预定义的接口对连接。这些接口对的示例包括 ICellProvider 和 ICellConsumer,以及 IRowProvider 和 IRowConsumer。
ASP.NET 2.0 中的 Web 部件连接模型比 WSS 中的旧式模型更容易、更灵活,因为您可以使用自己的自定义接口。这意味着您无需使用由 Microsoft 人员创建的接口定义。而且,您无需对接口对进行任何操作,它们必须由提供者和使用者实现。使用 ASP.NET 2.0,只有提供者需要实现一个接口。
要了解其工作方式,我们先在两个 Web 部件之间创建一个连接。对于我要在本月专栏中提供的示例,我决定使用 Northwind 数据库,因为它有一个 Customers 表和一个 Orders 表。这使我能为您展示如何针对主-从和一对多关系设计 Web 部件。有一点需要注意,如果使用 SQL Server® 2005,则在产品安装过程中并不安装该示例 Northwind 数据库。要安装它,您必须下载并运行 Microsoft Web 站点上可用的脚本(请参阅 Microsoft SQL Server 主页)。
现在,假设您要在一个显示客户列表的 Web 部件和一个显示当前所选客户的详细信息的客户 Web 部件之间建立一个 Web 部件连接,如图 1所示。显示客户列表的 Web 部件将扮演提供者的角色,而显示当前所选客户的详细信息的 Web 部件则作为使用者。在这种情况下,您希望提供者为使用者提供当前所选客户的 CustomerID 字段。
首先,创建一个名为 ICustomerIDProvider 的简单接口:
Public Interface ICustomerIDProvider
ReadOnly Property CustomerID() As String
End Interface
在本月专栏附带的代码示例中,我使用带有 SqlDataSource 和 GridView 控件的用户控件创建了提供者 Web 部件,以显示来自 Northwind 的客户。Web 部件源文件是 Customers.ascx 和 Customers.ascx.vb,如清单 2 所示。
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Partial Class WebParts_Customers
Inherits WebPartBase
Implements ICustomerIDProvider
Sub New()
Title = "NorthWind Customer List"
TitleIconImageUrl = "~\img\Customers.gif"
End Sub
<ConnectionProvider("Customer ID Provider")> _
Public Function GetCustomerProvider() As ICustomerIDProvider
Return Me
End Function
Public ReadOnly Property CustomerID() As String _
Implements ICustomerIDProvider.CustomerID
Get
If gridCustomers.SelectedDataKey Is Nothing Then
Return String.Empty
Else
Return Me.gridCustomers.SelectedDataKey.Value
End If
End Get
End Property
End Class
您可以看到,WebParts_Customers 作为提供者并实现用于该 Web 部件连接的接口。在本例中,WebParts_Customers 实现 ICustomerIDProvider 接口。虽然最常见的模式是提供者本身实现该连接接口,但是它不需要这么做。唯一的实际要求是,ConnectionProvider 方法返回指定接口的一个实例。因此,作为替代方案,该提供者 Web 部件可以返回一个实现该连接接口的 helper 对象。如果一个提供者 Web 部件具有多个接口类型相同的连接点,这通常是有必要的。
通过返回 GridView 控件的 SelectedDataKey 属性的值,WebParts_Customers 类实现 CustomerID 属性。GridView 控件已经进行了设置以便显示来自 Northwind Customers 表的记录,而且它还将 CustomerID 字段识别为 SelectedDataKey 值。
您应该注意到 WebParts_Customers 类有一个名为 GetCustomerProvider 的方法,该方法具有一个根据 ICustomerIDProvider 接口定义的返回类型。在这种情况下,由于 Web 部件本身实现所需的接口,因此 GetCustomerProvider 可以只返回对该类的当前实例的 Me 引用。您还要注意该方法已经使用 ConnectionProvider 属性进行了定义:
<ConnectionProvider("Customer ID Provider")>
WebPartManager 负责在运行时连接 Web 部件。当 WebPartManager 看到一个 Web 部件包含一个使用 ConnectionProvider 属性定义的方法时,它知道该 Web 部件公开一个连接点,因此可以作为提供者并且连接到使用者。当需要将两个 Web 部件连接在一起时,WebPartManager 将调用 GetCustomerProvider 方法获取对提供者 Web 部件的强类型引用。
定义一个提供者 Web 部件是否接受到使用者的多个连接是可能的。在某种情况下,一个提供者同时具有到多个使用者的连接是很有用的。在其他情况下,您可能想限制提供者,使它最多可以有一个到使用者 Web 部件的连接。默认情况下,提供者允许多个连接,而使用者则不然。要改变这一点,当您应用 ConnectionProvider 属性时,可以使用命名参数 AllowsMultipleConnections,如下所示:
<ConnectionProvider("Customer ID Provider", _
AllowsMultipleConnections:=False)>
既然您已经看到如何创建一个在提供者 Web 部件中公开连接点的方法,我们来看看这在使用者 Web 部件中是如何实现的。使用者 WebPart 通过提供一个使用 ConnectionConsumer 属性定义的方法来公开连接点。使用者的连接点方法与提供者的连接方法不同,因为它不定义返回值。相反,它获取一个用该连接的接口类型定义的参数:
<ConnectionConsumer("Customer ID Consumer")> _
Sub RegisterCustomerProvider(ByVal provider As ICustomerIDProvider)
... ' implementation
End Sub
请记住,提供者的连接点方法名称与使用者的连接点方法名称并不重要。唯一需要注意的是,每个方法分别使用 ConnectionProvider 属性和 ConnectionConsumer 属性进行定义。
现在,我们看看 WebPartManager 在运行时是如何建立该连接的。WebPartManager 调用提供者的连接点方法以获取对提供者对象的引用。接下来,WebPartManager 调用使用者的连接点方法,以便为它传递一个对提供者的强类型引用。
一旦 WebPartManager 完成它的工作之后,使用者 Web 部件就有一个返回到提供者 Web 部件的活动连接。此时,使用者可以使用该引用访问接口中定义的方法和属性,从而与提供者直接交互。但是,ASP.NET 小组建议使用者 Web 部件在 PreRender 阶段之前不应使用提供者接口上的方法和属性。具体说来,它们不应该在 方法本身中使用提供者接口上的方法和属性。原因是这些连接可能彼此依赖。您可能有一个连接到 ProviderConsumerWebPart 进而连接到 ConsumerWebPart 的 ProviderWebPart。在这两个连接建立之前,ConsumerWebPart 无法查询提供者接口,而且连接建立的顺序取决于框架。
Partial Class WebParts_CustomerDetails
Inherits WebPartBase
Sub New()
Me.Title = "Customer Details"
Me.TitleIconImageUrl = "~\img\Customer.gif"
End Sub
Private provider As ICustomerIDProvider
<ConnectionConsumer("Customer ID Consumer")> _
Sub RegisterCustomerProvider(ByVal provider As ICustomerIDProvider)
Me.provider = provider
End Sub
Protected Sub SqlDataSource1_Selecting(ByVal sender As Object, _
ByVal e As SqlDataSourceSelectingEventArgs) _
&nbs