面向开发人员的 Web 部件介绍
Andy Baron
MCW Technologies, LLC
适用于:
Microsoft Windows SharePoint Services
Microsoft Office SharePoint Portal Server 2003
Microsoft Visual Studio .NET
Web 部件基础结构
摘要:了解什么是 Web 部件以及如何创建它们。开发人员可以将 Web 部件构建为 ASP.NET 自定义控件。管理员可以在基于 Windows SharePoint Services 的任何站点上安装 Web 部件。通过在浏览器中拖放,用户可以将 Web 部件添加到页面中,还可以通过设置属性来个性化它们。Web部件可以通过标准接口连接到其他 Web 部件。
本文随附了一个示例 Visual Studio .NET 解决方案,其中包含两个用 C# 编写的自定义 Web 部件。借助于第一个 Web 部件,用户可以选择某个客户并查看有关该客户的可配置信息。借助于第二个 Web 部件,用户可以查看单个客户的定单。用户可以将这些 Web 部件添加到某个 Web 部件页中并将它们连接在一起,以便第二个 Web 部件可以显示第一个 Web 部件中所选客户的定单。
注 本文向开发人员介绍 Web 部件,而不对 Windows SharePoint Services 或 SharePoint Portal Server 进行介绍。有关详细信息,请参阅 SharePoint Products and Technologies。
本文中的信息还适用于在 Windows SharePoint Services 平台上构建的 Microsoft Office SharePoint Portal Server 2003。本文附带的代码示例在加载到用 SharePoint Portal Server 创建的站点中时,应当能够正常工作。
从 Microsoft 下载中心下载 IntroWebPartsCode.exe。
本页内容
背景知识
近几年来,在 Microsoft 内部一直有开发强有力的搜索技术的构思。那就是使信息工作者只需通过拖放即插即用组件来创建个性化用户界面。非编程人员应能够将他们所关心的信息集合在一起并自定义其外观。他们应能够在一个位置上组合一个图形,并在其中显示他们部门的销售额、一个本地通信监视器、一个股票报价机、一个针对所选主题的新闻提要,可能还包含一个显示每日约会的日历。
在第一个具体实现中,这种想法最初被命名为数字仪表板,它的第一个应用环境是 Microsoft Outlook。最终,浏览器演变成可插入 Web 部件的首选容器,这种想法融合到行业范围的趋势(即,开发可自定义的 Web 门户)中。
数字仪表板和 Web 部件的早期实现在主题演讲中受到广泛吹捧,并引起了具有前瞻看法的开发人员的兴趣。但是,早期技术很难实现,其性能、可伸缩性、可维护性、本地化功能和安全性都不够完善。
与此同时,在 Microsoft 中有两个 Web 技术创新活动正在迅速推广。ASP.NET 是在新兴的公共语言运行库的基础上构建的,它为 Web 开发人员提供一个高级框架,以使用面向对象的技术来创建快速可靠的站点。同样重要的是,Microsoft SharePoint? 产品和技术组正开发一个高级结构,用于构建和维护基于浏览器的协作式工作区。
ASP.NET 和 Microsoft Windows SharePoint Services(它们均与 Microsoft Windows Server 2003 捆绑在一起)一同为 Web 部件的全新实现提供了平台,该平台就是基于最初的想法构建的。
Web 部件基础结构
新的 Web 部件基础结构通过提供一个 .NET 对象模型(其中包含从 ASP.NET 类派生并对其进行扩展的类)在 ASP.NET 的基础上构建。其他类和数据库对象负责存储(在 Microsoft SQL Server? 或 MSDE 中)和站点管理。
Web 部件是 ASP.NET 服务器控件。要创建新类型的 Web 部件,您可以创建一个 ASP.NET 自定义控件。然而,与标准 ASP.NET 控件(由程序员在设计时添加到 Web 窗体页中)不同的是,Web 部件将由用户在运行时添加到 Web 部件页上的 Web 部件区域。
根据将用户分配到的站点组以及分配给这些组的权限,用户在修改 Web 部件和 Web 部件页时可以具有不同的自由度。他们可以进行适用于某个共享页所有用户的更改,也可以进行仅在其查看该页时适用的更改。
在许多方面,Web 部件使设计时和运行时之间的传统区别变得很模糊。对于在浏览器中使用 Web 部件的用户来说,其运行时体验类似于 Microsoft Visual Basic 程序员在向窗体中添加控件并设置其属性时的设计时体验。Web 页设计者也可以在 Microsoft Office FrontPage疇2003 中构建 Web 部件页,以便能够在设计环境中呈现 Web 部件。
Web 部件在很大程度上依赖 Windows SharePoint Services 来支持下列操作:
• | 创建新站点和新页面 |
• | 管理站点的用户名单 |
• | 存储 Web 部件的自定义(包括共享属性设置和个人属性设置) |
• | 管理站点备份和存储限制 |
• | 可处理数千个站点和上百万个用户的可伸缩体系结构 |
• | 将用户分配给可自定义的站点组 注 Microsoft SharePoint 产品和技术不再依赖基于角色的安全性为用户分配权利和权限。相反,SharePoint 产品和技术使用站点组和跨站点组为用户分配权利和权限。站点组是适用于特定 Web 站点的自定义安全组,跨站点组是适用于多个 Web 站点的自定义安全组。有关详细信息,请参阅 Microsoft Windows SharePoint Services 帮助。 |
并且,SharePoint 产品和技术依赖 Web 部件来提供可配置和可扩展的用户界面。
对于 Web 部件来说,最有趣、最具创新的新功能可能是一个基础结构,Web 部件可以在其中使用标准接口来互相通信。用户可以使用简单的菜单选项来连接能够交换数据的 Web 部件,而且 Web 部件在开发时可以完全相互独立。例如,一个供应商的图形 Web 部件可以连接到另一个供应商的数据表视图 Web 部件,或者,一个股票报价 Web 部件可以显示另一个列出供应商的 Web 部件中选定公司的当前股票价格。
安装 Web 部件
本文附带了一个下载文件,其中包含一个以 Microsoft Visual Studio .NET 2002 创建的解决方案。如果您使用的是 Visual Studio .NET 2003,则可以打开该解决方案,它将自动进行升级。
示例解决方案的名称为 Northwind,其中包含两个项目。第一个项目的名称也为 Northwind,它输出一个名为 Northwind.dll 的 Web 控件库,其中包含两个 Web 部件控件。该解决方案中的第二个项目是一个名为 NorthwindCab 的 Cab 项目,它输出一个名为 NorthwindCab.cab 的 .cab 文件。使用这个 .cab 文件,可以在运行 Microsoft Windows Server 2003 和 Windows SharePoint Services 的服务器上安装示例 Web 部件。
NorthwindCab.cab 文件的发行版本位于示例文件的 /Northwind/NorthwindCab/Release 目录中。要从这个 .cab 文件安装 Web 部件,请使用位于服务器以下目录中的 Stsadm.exe 命令行工具:
local_drive:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/60/bin
用具有管理权限的帐户登录服务器,打开命令提示符,然后运行以下命令:
stsadm.exe –o addwppack –filename path_to_NorthwindCab.cab
警告 要允许示例 Northwind Web 部件从 XML 文件加载数据,您可能需要在运行 Windows SharePoint Services 的服务器上调整安全策略设置。有关执行此操作的详细信息,请参阅本文后面的代码访问安全性部分。
向 Web 部件页中添加 Web 部件
Windows SharePoint Services 提供四种可包含 Web 部件的库:
• | 虚拟服务器库 |
• | <Site Name=>库 |
• | Web 部件页库 |
• | 联机库 |
虚拟服务器库列出可供服务器上所有站点使用的 Web 部件。<Site_Name> 库包含可供特定站点使用的 Web 部件。在默认情况下,当您运行 Stsadm.exe 来安装某个 Web 部件时,Stsadm.exe 会将该 Web 部件添加到虚拟服务器库中。有关如何使用 Windows SharePoint Services 中提供的管理工具来填充<Site_Name> Web 站点库的详细信息可在本文的后面找到。
Web 部件页库包含已经添加到当前页面中的 Web 部件,这乍看起来很荒谬。为什么需要添加已经添加到页面上的 Web 部件呢?这之所以有用,是因为您可以将某个 Web 部件添加到页面中,然后关闭该 Web 部件。要关闭某个 Web 部件,请单击该 Web 部件标题栏右侧的箭头,然后单击“关闭”。已关闭的 Web 部件不再出现在该页面上,但它仍是该页面 Web 部件页库的成员。此 Web 部件仍然通过运行 Windows SharePoint Services 的服务器的配置数据库中的某个项目与该页面相关联,此页面还存储该 Web 部件的所有共享属性设置或个性化属性设置。
您可以将 Web 部件添加到页面中,以独占方式个性化该部件,关闭该部件,然后将其重新添加到该页中并使个性化设置保持不变。要取回已关闭的 Web 部件,请从 Web 部件页库中选择它。
注 要将某个 Web 部件从页面中实际删除,您必须单击“修改我的网页”菜单(或共享视图中的“修改共享网页”菜单),然后单击“设计此网页”,将“删除”命令添加到该 Web 部件的下拉菜单中。要将该 Web 部件从页面中删除,而不将其添加到 Web 部件页库中,请单击该 Web 部件标题栏右侧的箭头,然后单击“删除”。
联机库是一组可通过 Web 服务使用的 Web 部件,它允许许多服务器共享访问一个集中维护的公共 Web 部件库。该 Web 服务的 URL 在站点的 Web.config 文件的 OnlineLibrary 元素中指定。
要点 要启用联机库,您必须编辑服务器上的 Web.config 文件,并将 OnlineLibrary 元素的 URL 特性更改为以下特性:
<OnlineLibrary Url="http://officebeta.microsoft.com/gallery/gallery.aspx"/>
要点 如果您使用代理服务器,还必须添加以下部分:
<system.net> <defaultProxy> <proxy proxyaddress="http://server_name:port_number" bypassonlocal = "true"/> </defaultProxy> </system.net>
要将上述四个默认库的其中一个内出现的 Web 部件添加到 SharePoint 页中,请按照下列步骤操作:
1. | 要将 Web 部件只添加到自己版本的页面中,请确保“修改我的网页”菜单出现在页面顶部。如果“修改共享网页”菜单出现在页面顶部,请单击“修改共享网页”菜单,然后单击“个人视图”。 要将 Web 部件添加到所有用户的页面中,请确保“修改共享网页”菜单出现在页面顶部。如果“修改我的网页”菜单出现在页面顶部,请单击“修改我的网页”菜单,然后单击“共享视图”。 |
2. | 在“修改我的网页”菜单(个人视图)或“修改共享网页”菜单(共享视图)上,指向“添加 Web 部件”,然后单击“浏览”或“搜索”。 注 如果您单击“浏览”,则可以选择这四个库中的任意一个,以便查看该库中 Web 部件的列表。如果您单击“搜索”,则可以将选择限制在名称中包含搜索文本的 Web 部件范围内。“浏览”和“搜索”命令都可以打开四个可用库的列表,并为每个库显示一个数字,指出该库中可用 Web 部件的数量。当您选择某个库时,会出现该库中 Web 部件的列表。 |
3. | 将希望添加的 Web 部件从任务窗格拖到 Web 部件页的 Web 部件区域中,然后可以关闭任务窗格或选择更多的 Web 部件。 |
图 1 显示正从虚拟服务器库拖到页面上的示例 CustomerCellConsumer Web 部件。正如本文前面所述的那样,该库是在使用 Stsadm.exe 工具时,在其中安装示例 Web 部件的库。
图 1. 在 Internet Explorer 中向页面添加 Web 部件
向页面添加 Web 部件时,可以在支持丰富的用户交互功能的浏览器中使用拖放操作。但是,Web 部件不需要 Microsoft Internet Explorer,甚至也不需要支持动态 HTML 的浏览器。其中一个原因在于,图 1 中显示的任务窗格包含一个用于在页面上选择 Web 部件区域的下拉列表,以及一个用于将所选的 Web 部件添加到选定区域的“添加”按钮。另外,这些替换控件可让不使用鼠标的用户访问 Web 部件。
注 本文中的几幅图显示了站点主页上的 Web 部件快照。您在将 Web 部件添加到站点主页中时不受限制。要创建新的 Web 部件页,请在菜单栏中单击“创建”,滚至“创建页面”页底部的“网页”标题,然后单击“Web 部件页”。在“新建 Web 部件页”页上,您可以命名新页面,从列表中选择一个页面布局,还可以选择包含这个新页的文档库。然后,您可以向这个新页中添加 Web 部件。
设置 Web 部件的属性
Web 部件可共享公共属性,如 Title、Height 和 AllowClose。在其中出现 Web 部件的区域也是 Web 部件的一个属性。您可以通过将标准 .NET 属性添加到 Web 部件的类中,来向 Web 部件添加自定义属性。
当您在浏览器中设置 Web 部件的属性时,修改范围取决于页面是位于个人视图还是共享视图中。在个人视图中进行的更改仅适用于当前用户,且优先于在共享视图中进行的更改。可使用共享视图为该页的所有用户设置默认值。
要更改某个 Web 部件的属性,请单击该 Web 部件标题栏右侧的箭头,然后单击“修改我的 Web 部件”(在个人视图中)或“修改共享 Web 部件”(在共享视图中)。您无需将页面切换到设计视图,即可进行该选择。但是,当出现用于设置属性的任务窗格时,页面会自动更改到设计视图。在设计视图中,您可以看到每个 Web 部件区域的轮廓以及该区域的标题。
示例 CustomerRowProvider Web 部件有一个自定义布尔属性 DetailViewEnabled,该属性可决定在该 Web 部件中显示多少有关所选客户的信息。图 2 显示属性如何显示在浏览器的任务窗格中。
图 2. 在 Internet Explorer 中设置自定义属性
在 C# 代码中,该属性的特性(在本文的后面介绍)提供该属性的自定义视图类别,以及出现在任务窗格中的友好名称和工具提示。因为该属性有一个布尔数据类型,所以 Web 部件基础结构可自动为该属性创建一个复选框控件。
连接 Web 部件
Web 部件基础结构为 Web 部件之间的通信提供了强大的支持。开发人员可以使用标准接口来创建可互相交换信息的 Web 部件。例如,用来实现 ICellConsumer 接口的 Web 部件可以从用来实现 ICellProvider 接口的 Web 部件接收信息。用户可以使用浏览器中的简单菜单命令将 Web 部件互相连接。
另外,Web 部件基础结构还提供了转换器,从而允许接口不完全互补的 Web 部件互相通信。Web 部件基础结构可自动检测接口不匹配的情况并显示一个对话框,该对话框允许用户将一个接口的值映射到另一个接口。例如,如果用户将用来实现 IRowProvider 接口的 Web 部件连接到用来实现 ICellConsumer 接口的 Web 部件,就会显示一个对话框,允许用户从 IRowProvider Web 部件中选择要发送到 ICellConsumer Web 部件的单列数据。示例 Northwind Web 部件中提供了这样的示例。
要在将 CustomerRowProvider 和 OrderCellConsumer Web 部件添加到页面之后将它们连接在一起,请按照下列步骤操作:
1. | 如果页面尚未处于设计视图中,请单击“修改我的网页”菜单或“修改共享网页”菜单,然后单击“设计此网页”。 注 只有当页面处于设计视图时,才能创建连接。 |
2. | 单击 Orders Web 部件标题栏中的箭头,指向“Connections”,指向“Get Customer ID from”(该标签分配给由此 Web 部件提供的唯一连接接口),然后单击“Customer (Row Provider)”,如图 3 所示。请注意,页面上的几个 Web 部件会出现在这个子菜单中,这是因为它们都实现与 Orders Web 部件的 ICellConsumer 接口相兼容的连接接口。 图 3. 在 Internet Explorer 中连接 Web 部件 |
3. | 在选择了要连接的 Web 部件之后,会出现一个对话框,如图 4 所示。创建此对话框无需任何代码。此对话框会自动出现,请求将 ICellConsumer Web 部件连接到 IRowProvider Web 部件所必需的额外信息。 图 4. 一个对话框请求创建连接所需的信息。 |
4. | 选择 Customer ID(这是默认选项,因为它是列表上的第一个选项),然后单击“Finish”。 |
5. | 要查看 Orders Web 部件中特定客户的定单,请在 Customer Web 部件中选择一个客户 ID,如图 5 所示。 图 5. Orders Web 部件显示 Customer Web 部件中所选客户的定单。 |
您可能想知道 Customer Web 部件为什么不实现 ICellProvider 接口以提供客户 ID,而是需要一个转换器来连接到 Orders Web 部件。通过实现 IRowProvider 接口(而非 ICellProvider 接口),Customer Web 部件会更通用。它可以与用来实现 IRowConsumer 或 IFilterConsumer 接口的 Web 部件进行连接,也可以与各种 ICellConsumer Web 部件进行连接。例如,Customer Web 部件可以将其 PostalCode 列中的数据提供给 ICellConsumer Web 部件(该部件显示指定邮政编码地区的天气状况)。
在浏览器中,可以使用四对接口来连接 Web 部件:
• | ICellProvider/ICellConsumer 此接口对可与单个单元格中的数据进行通信。通过使用转换器,ICellConsumer 还可以与 IRowProvider 进行连接。 |
• | IRowProvider/IRowConsumer 此接口对可与一行数据进行通信。通过使用转换器,IRowProvider 可以与 ICellConsumer 和 IFilterConsumer 进行连接。 |
• | IListProvider/IListConsumer 此接口对可与整个数据列表进行通信。 |
• | IFilterProvider/IFilterConsumer 此接口对可与包含一对或多对列名和值的筛选器表达式进行通信。IFilterConsumer 可以使用转换器与 IRowProvider 进行连接,但是这种类型的连接只能处理一对列名/值。换言之,如果您使用转换器连接到 IRowProvider,则只能针对单列进行筛选。 |
另外,在 FrontPage 2003 中(而不是在浏览器中),可以使用两个接口对来连接 Web 部件。您还可以使用下列接口来创建跨页面连接,这些连接使用查询字符串来交换数据:
• | IParametersOutProvider/IParametersOutConsumer IParametersOutProvider Web 部件定义了一组参数,它可以将这些参数发送到与之兼容的 IParametersOutConsumer Web 部件。通过使用转换器,IParametersOutProvider Web 部件还可以与 IParametersInConsumer Web 部件进行连接。 |
• | IParametersInProvider/IParametersInConsumer IParametersInConsumer Web 部件定义了一组参数,它可以从与之兼容的 IParametersInProvider Web 部件接收这些参数。通过使用转换器,IParametersInConsumer Web 部件还可以与 IParametersOutProvider Web 部件或 IRowProvider Web 部件进行连接。 |
IParametersOut 和 IParametersIn 接口对之间的区别可能会令人混淆。二者的重要区别在于,提供者为 IParametersOut 接口对定义参数,而使用者为 IParametersIn 接口对定义参数。当用户尝试将 IParametersOutProvider Web 部件连接到 IParmetersInConsumer Web 部件时,会出现一个对话框,用户可以使用该对话框定义如何将提供者 Web 部件的参数映射到使用者 Web 部件的参数。这些接口的通用性很强,但请记住,用户必须使用 FrontPage 2003 才能连接这些接口,而不能在浏览器中连接这些接口。
下表显示可以通过转换器互相连接的接口对,以及这些连接是否需要 FrontPage 2003。
可通过转换器进行连接 | 可在浏览器中进行连接 | 可在 FrontPage 中进行连接 |
IRowProvider 到 ICellConsumer | 是 | 是 |
IRowProvider 到 IFilterConsumer | 是 | 是 |
IParametersOutProvider 到 IParametersInConsumer | 否 | 是 |
IRowProvider 到 IParametersInConsumer | 否 | 是 |
下表显示可跨页面进行连接的接口。要连接不同页面上的 Web 部件,您必须使用 FrontPage 2003。
源页面接口 | 目标页面接口 |
IRowProvider | IFilterConsumer |
IRowProvider | IParametersInConsumer |
IFilterProvider | IFilterConsumer |
IParametersOutProvider | IParametersInConsumer |
IParametersInProvider | IParametersInConsumer |
在 Visual Studio .NET 中使用 Web 部件模板
所有的 Web 部件都是 ASP.NET 自定义控件,因此 Visual Studio .NET 可为创建和调试 Web 部件提供绝佳的环境。
要在 Visual Studio 中为 Web 部件自定义控件创建项目,您可以使用用来构建 Web 控件库的标准模板,也可以使用用来构建 Web 部件库的特殊模板。Web 部件库模板不随附在 Visual Studio .NET 中,它们可从 Web Part Templates for Microsoft Visual Studio .NET 中免费下载。
Web 部件库模板对于 C# 和 Visual Basic .NET 均可用。在运行模板的安装程序时,您可以选择要安装的语言。要安装模板,您还必须指定 Microsoft.SharePoint.dll 文件的路径。在默认情况下,Microsoft.SharePoint.dll 文件安装在运行 Windows SharePoint Services 的计算机的以下位置:
local_drive:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/60/ISAPI
与使用标准的 Web 控件库模板相比,使用 Web 部件库模板有几个好处。从 Web 部件库模板生成的项目自动包括以下有用的项目和功能:
• | 对 Microsoft.Sharepoint.dll 文件的引用 | ||||||||||
• | Web 部件类文件和与之匹配的 .dwp 文件(.dwp 文件是 XML 文件,它们引用 Web 部件的程序集、命名空间、类名以及 Web 部件的可选属性设置)。 | ||||||||||
• | 能够添加包含以下类型项目的示例代码的文件:
|
设置 Web 部件项目的输出路径属性
当您在 Visual Studio .NET 中创建 Web 部件时,必须更改“输出路径”属性,使其指向 Windows SharePoint Services 站点的 bin 文件夹,即使在使用 Web 部件库模板创建项目时也是如此。如果您希望对本文附带的示例 Northwind 项目进行更改、重新编译并测试所作的更改,则也需要进行上述更改。在默认情况下,“输出路径”属性指向项目目录中的本地 Bin 文件夹。
要在 Visual Studio .NET 中编辑“输出路径”属性,请按照下列步骤操作:
1. | 在解决方案资源管理器中,右键单击该项目,然后单击“属性”。 |
2. | 在左窗格中,单击“配置属性”,然后单击“生成”。 |
3. | 在“输出”下,单击“输出路径”属性值,键入虚拟服务器上 Bin 文件夹的路径(或者单击省略号 (...) 按钮,以浏览至 Bin 文件夹所在的位置),然后单击“确定”。 |
该路径会从绝对路径转换为相对路径,如图 6 所示。
图 6. 编辑“输出路径”属性以指向站点的 Bin 文件夹。
当您重新编译项目时,编译器会自动将包含 Web 部件程序集的 .dll 文件放在由“输出路径”属性指定的目录中。如果该目录中已经存在同名的文件,则它会被新文件替换。
警告 如果您在编辑“输出路径”属性之前重新编译示例 Northwind 项目,则编译器会创建一组文件夹,这些文件夹与原始属性设置(可能与 SharePoint 站点的 Bin 目录所在的位置不对应)指定的相对路径相匹配。
将 Web 部件用作 ASP.NET 自定义控件
Web 部件的创建过程不同于 ASP.NET 自定义控件的创建过程。因此,在钻研如何创建 Web 部件的细节之前,快速回顾一下 ASP.NET 可扩展模型将有所帮助。
ASP.NET 为扩展服务器控件框架提供了两个抽象:用户控件和自定义控件。
用户控件实质上是可插入其他页面中的 ASP.NET 页面,它们在一定程度上类似于传统 ASP 中使用的 Include 文件。使用 Visual Studio .NET,您可以使用将控件拖到页面设计器的方式,将服务器控件拖到用户控件设计器上,从而轻松地构建用户控件。用户控件会创建一些 .ascx 文件,这些文件非常类似于在构建 ASP.NET 页面时创建的 .aspx 文件。但是,您不能通过构建用户控件来创建 Web 部件。要创建 Web 部件,您必须创建 ASP.NET 自定义控件。
与用户控件不同的是,ASP.NET 自定义控件不受 Visual Studio .NET 中图形工具的支持。要构建自定义控件,您必须创建一个从 System.Web.UI.Control 直接或间接继承的类,还必须编写自己的代码来发出 HTML。ASP.NET 附带的所有服务器控件都是以这种方式创建的。为了帮助您创建自定义控件,ASP.NET 框架提供了 HtmlTextWriter 类。HtmlTextWriter 类有许多方法、属性和附带枚举,您可以使用它们轻松地生成可靠的 HTML,而不只是构造字符串。ASP.NET 基础结构对页面上所有控件的呈现进行协调。
当您创建从 Control 类直接继承的 ASP.NET 自定义控件时,可以通过替代基类的 Render 方法来生成 HTML。此方法将 HtmlTextWriter 对象作为参数。此对象由 ASP.NET 基础结构在运行时传递给该方法,代码会通过调用该 HtmlTextWriter 对象上的方法来发出 HTML。
注 为了避免混淆,请记住,ASP.NET 使用“呈现”一词,这不同于通常的用法。呈现通常是指将 HTML 文本转换为图形显示,此任务通常由浏览器执行。但是,在 ASP.NET 中,呈现是指在服务器控件中生成 HTML。
在创建 ASP.NET 自定义控件时,建议不要直接从 System.Web.UI.Control 类继承。从 System.Web.UI.WebControls.WebControl(它本身从 System.Web.UI.Control 类继承)继承通常会更高效、更可靠。WebControl 类包括与公用样式相关的属性(如 BackColor、BorderWidth 和 Font 等)的实现,并负责将这些属性呈现为 HTML 特性。
当您创建从 WebControl 类派生(而不是直接从 Control 类派生)的类时,不要替代 Render 方法,而是替代一个名为 RenderContents 的方法,该方法是 WebControl 类的方法。WebControl 类替代 Render 方法来为控件创建外部标记,其特性与其样式相关的属性相对应。替代控件中的 RenderContents 方法使您可以发出 HTML,该 HTML 已正确嵌入到由控件的 Render 方法创建的标记中。RenderContents 方法将 HtmlTextWriter 对象作为参数,正如 Render 那样。
Web 部件自定义控件的开发遵循相似的模式。但是,您应创建一个从 Microsoft.SharePoint.WebPartPages.WebPart 类继承的类,而不是创建从 WebControl 类继承的类。这个 WebPart 类从 System.Web.UI.Control 类继承,正如 WebControl 那样。WebPart 类负责创建控件的铬印效果(例如,标题栏和边框),用户可以通过设置属性和应用主题来自定义铬印效果。WebPart 类还处理与 WebPartPage 和 WebPartZone 类的交互,从而支持在页面上添加、移动、隐藏、删除、连接、自定义和个性化 Web 部件。图 7 显示 WebPart 类的层次结构,并显示 Web 部件与其他自定义控件的比较。
图 7. Web 部件派生自 Microsoft.SharePoint.WebPartPages.WebPart 类,该类派生自 System.Web.UI.Control。
如果您曾经创建过 ASP.NET 自定义控件,则会发现 Web 部件的创建过程非常熟悉。在书籍、文章及示例代码中演示的适于自定义控件的方法,同样适用于 Web 部件自定义控件的构建任务。只需记住从 WebPart 类(而非 WebControl)继承,以及替代 RenderWebPart 方法(而非 RenderContents 方法)。
在标准的自定义控件中,如果您确实需要直接替代 Render 方法,则可以选择这样做,即使您从 WebControl 类继承也是如此。但是,在 WebPart 类中,Render 方法是密封的。在从 WebPart 派生的类中,您必须替代 RenderWebPart,而不是 Render。
与其他自定义 ASP.NET 控件相比,Web 部件基础结构为 Web 部件提供了以下几点优势:
• | 用户可以将 Web 部件库中的 Web 部件添加到 Web 部件区域中。 |
• | 用户可以修改 Web 部件的个人属性或共享属性,并使所作的更改保持稳定。 |
• | Web 部件可以使用标准接口相互连接,用户可以启动这些连接。 |
Web 部件可参与和其他 ASP.NET 服务器控件相同的执行周期。有关此周期中各个阶段的概述以及指向更详细信息的链接,请参阅 Control Execution Lifecycle。
创建 Web 部件类
本文附带的两个示例 Web 部件(CustomerRowProvider 和 OrdersCellConsumer)包含有注释代码,您可以将该代码用作创建自己的 Web 部件的指南。
在创建 Web 部件控件时,第一步是定义一个从 WebPart 类继承的类,该类可以实现一个或多个 Web 部件通信接口。下面的代码包括 CustomerRowProvider Web 部件的类声明:
using System; using System.Data; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; using System.Xml.Serialization; using System.Runtime.InteropServices; using Microsoft.SharePoint.WebPartPages; using Microsoft.SharePoint.WebPartPages.Communication; namespace Northwind { [DefaultProperty("Text"), ToolboxData("<{0}:CustomerRowProvider runat=server></{0}:CustomerRowProvider>"), XmlRoot(Namespace="Northwind")] public class CustomerRowProvider : WebPart, IRowProvider {
该类应用了三个特性。如果您在 Visual Studio .NET 中使用 Web 部件模板或 Web 控件模板,则这些特性会自动插入。前两个特性(DefaultProperty 和 ToolboxData)控制设计环境(如 Visual Studio)中的行为 — 它们指定属性窗口中默认选择的属性,以及插入 ASPX 或 ASCX 文件的文本(如果将控件拖到页面或用户控件上)。{0} 标记由 Page 指令(或者对于用户控件来说,是 Control 指令)中已注册到控件的标记前缀替换。
这强调了一个事实,即可以像对任何其他服务器控件那样,将 Web 部件控件拖到 ASP.NET Web 窗体页上。但是,使用此方法将 Web 部件直接嵌入 ASP.NET 页不是一个好办法,因为这会失去 Web 部件基础结构的功能 — 用户不能保存对个人属性或共享属性进行的更改,也不能将 Web 部件互相连接在一起。要实现此功能,必须将 Web 部件添加到 Web 部件区域中,这是在浏览器或 FrontPage(而非 Visual Studio)中完成的。
XmlRoot 特性也是由模板添加的 — 在将基于此类的对象序列化为 XML 时,该特性会生效。
OrdersCellConsumer 类的类定义类似于上面的代码示例,唯一的区别在于它实现 ICellConsumer,而非 IRowProvider。
向 Web 部件中添加子控件
即使 Web 部件服务器控件中的所有 HTML 呈现都是通过在 RenderWebPart 中调用 HtmlTextWriter 对象的方法来执行的,您仍然可以在 Web 部件内部使用 ASP.NET 控件,这些控件被称作子控件。您可以使用在 ASP.NET 页的类中相同的方式为这些子控件创建变量。
CustomerRowProvider 代码包含了几个 ASP.NET 子控件的声明、一个下拉列表和十个标签:
protected DropDownList CustomerIdChooser; protected Label CompanyNameLabel; protected Label ContactNameLabel; protected Label ContactTitleLabel; protected Label AddressLabel; protected Label CityLabel; protected Label PostalCodeLabel; protected Label CountryLabel; protected Label PhoneNumberLabel; protected Label FaxNumberLabel; protected Label ErrorLabel;
要在 Web 部件中使用这些子控件,您必须替代 System.Web.UI.Control 的 CreateChildControls 方法。在该方法中,您可以实例化这些控件,设置它们的属性,并挂钩任何事件处理程序。您还必须将这些控件添加到从 System.Web.UI.Control 继承的 Web 部件的 Controls 集合属性中。该代码首先实例化 ErrorLabel 控件,该控件在默认情况下不可见。如果在尝试加载 Web 部件的数据时出现异常,该控件会显示一则错误消息。
protected override void CreateChildControls() { ErrorLabel = new Label(); ErrorLabel.Visible = false; Controls.Add(ErrorLabel);
接着,该代码将实例化下拉列表控件,挂钩它的事件处理程序,并将该控件添加到 Web 部件的 Controls 集合中:
CustomerIdChooser = new DropDownList(); CustomerIdChooser.Load += new EventHandler(CustomerIdLoad); CustomerIdChooser.SelectedIndexChanged += new EventHandler(CustomerIdChanged); Controls.Add(CustomerIdChooser);
使用类似的代码实例化其余的标签控件,设置它们的属性,并将它们添加到 Controls 集合中。
在 ASP.NET Web 窗体或用户控件中,可以将某些控件属性设置指定为 .ASPX 或 .ASCX 文件中的特性。但是,与在任何自定义控件中一样,在 Web 部件中,必须在代码中指定所有的属性设置。这类似于 .NET Windows 窗体中使用的模式,在该模式中,所有的控件属性设置都出现在代码中,并且所有的控件都显式添加到窗体的 Controls 集合属性中。
CustomerRowProvider 代码包含下拉列表的两个事件处理程序 — 一个用来加载列表,另一个用来对选择做出响应。在 Load 事件处理程序中,该代码将 XML 文件中的架构和数据读入 ADO.NET 数据集中。该代码假设 XML 文件位于 Wpresources 目录中。每个 Web 部件都能够访问此目录,因此最好将图像、本地化资源或 Web 部件所需的其他文件放在此处。但是,要访问任何文件(即使是存储在 Wpresources 目录中的文件),您的代码必须具有充分的权限。用于确保向 Web 部件分配充分权限的各种选项将在本文的后面讨论。
ReadXml 方法需要的是文件路径(而非 URL),因此该代码使用 Server.MapPath 方法将 Wpresources 目录的相对路径映射到服务器上的实际文件路径。该代码包装在 try/catch 块中,因此,当文件 I/O 权限不可用时,它可以做出正确的响应:
public void CustomerIdLoad(object sender, EventArgs e) { try { string customerXmlFile = Page.Server.MapPath(@"/wpresources/Northwind/Customers.XML"); CustomersSet.ReadXml(customerXmlFile, XmlReadMode.ReadSchema); } catch (System.Security.SecurityException ex) { ErrorLabel.Text = ex.Message + "<br>" + "Steps to correct this are included in" + " the documentation for this sample."; ErrorLabel.Visible = true; return; } catch (Exception ex) { ErrorLabel.Text = ex.Message; ErrorLabel.Visible = true; return; } // No error if we made it this far. ErrorLabel.Visible = false;
只有当首次加载页面时,该代码才需要填充下拉列表。在回发请求的过程中,列表会自动从 ViewState 属性加载,正如在任何 ASP.NET 页面上一样。但是,您不能依赖在 ASP.NET 页面中(甚至在 ASP.NET 自定义控件中)编写代码时使用的方式,检查页面的 IsPostBack 属性值。
您确实可以使用以下方法来访问 IsPostBack 属性:this.Page.IsPostBack。但是,在将某个 Web 部件添加到页面中时,会出现回发请求,即使在该 Web 部件的 ViewState 数据尚未填充时也是如此。对于直接嵌入 ASP.NET 页面的服务器控件,决不会出现这种类型的回发请求,回发请求只出现在 Web 部件已加载过一次之后。这强调了 Web 部件和其他自定义控件之间的微妙区别:用户可以在运行时将 Web 部件添加到页面中,该操作可触发回发请求。
下面的示例代码检查列表中是否已包含任何项目,而不是检查 IsPostBack 属性:
if (CustomerIdChooser.Items.Count==0) { // Bind data. CustomerIdChooser.DataSource = CustomersSet.Tables["Customers"]; CustomerIdChooser.DataTextField = "CustomerID"; CustomerIdChooser.DataValueField = "CustomerID"; CustomerIdChooser.DataBind(); // Add an instruction item at the top of the list. CustomerIdChooser.Items.Insert(0, new ListItem("-Select-", "")); // Trigger a postback when a customer is selected. CustomerIdChooser.AutoPostBack = true; } }
列表的 SelectedIndexChanged 事件代码是任一标准 ASP.NET 页面的典型代码,因此它未包括在此处。该代码可在客户被选定时填充 Web 部件的标签。
呈现 Web 部件的 HTML
在 Web 部件中使用子控件是可选的,但是,向用户显示内容的每个 Web 部件都必须替代 RenderWebPart 方法,Web 部件正是在此处生成在 Web 部件框架中呈现的 HTML。
如果您的 Web 部件使用子控件,则应通过调用 EnsureChildControls 来开始执行 RenderWebPart 方法。System.Web.UI.Control 的 RenderWebPart 方法会自动检查 ChildControlsCreated 属性。如果该属性为 false,此方法会调用 CreateChildControls。EnsureChildControls 可作为一种安全方式在 Web 部件代码中的许多位置上调用,这样可确保当您需要子控件时,它们就位于相应的位置,而不必多次创建它们:
protected override void RenderWebPart(HtmlTextWriter output) { EnsureChildControls();
CustomerRowProvider Web 部件包含一个名为 ErrorLabel 的标签,该标签在默认情况下处于隐藏状态。只有在尝试读取 CustomerIdLoad 事件处理程序中的 XML 数据文件的过程中出现异常时,该标签才变得可见。
确保已创建所有子控件之后,RenderWebPart 代码会检查 ErrorLabel 是否仍旧不可见,从而指出数据是否已成功加载:
if (ErrorLabel.Visible == false) {
在编写用来在 RenderWebPart 中发出 HTML 的代码时,涉及到调用传递到 RenderWebPart 方法的 HtmlTextWriter 对象的方法。要创建某个 HTML 标记,首先需要调用 AddAttribute 方法以定义要添加到该标记中的所有特性,然后调用 RenderBeginTag 方法并指定要创建的 HTML 元素。在需要关闭某个标记时,调用 RenderEndTag 来关闭打开的最后一个标记。
CustomerRowProvider 的呈现代码通过创建一个 div 标记开始,该标记指定单元格填充值为 2 个像素:
output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "2px"); output.RenderBeginTag("div");
还有一个更可靠的方法来创建此标记。您可以使用 HtmlTextWriterTag 枚举,而不是对标记的名称进行硬编码:
output.RenderBeginTag(HtmlTextWriterTag.Div);
枚举值可用于所有常见的 HTML 标记,您可以使用它们针对标记的拼写错误来提供编译时保护。
如果您需要设置 HTML 标记的多个特性,则可以在调用 RenderBeginTag 之前多次调用 AddAttribute。指定的所有特性值都添加到开始标记中。下面的代码设置了 table 标记的几个特性:
output.AddAttribute(HtmlTextWriterAttribute.Width, "400px"); output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "2px"); output.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "1px"); output.RenderBeginTag("table");
当 Web 部件在浏览器中呈现时,上面的代码可创建下面的 HTML:
<table width="400px" cellpadding="2px" cellspacing="1px">
您还可以通过调用 AddStyleAttribute(而非 AddAttribute)来创建 CSS 样式的标记,这些标记可将几个设置组合到一个特性中:
output.RenderBeginTag("tr"); output.AddStyleAttribute(HtmlTextWriterStyle.FontWeight, "bold"); output.AddStyleAttribute(HtmlTextWriterStyle.Width, "200px"); output.RenderBeginTag("td");
上面的几行代码可发出下面的 HTML:
<tr> <td style="font-weight:bold;width:200px;">
另外,您可以通过调用 HtmlTextWriter 对象的 Write 方法来发出硬编码的 HTML 字符串:
output.Write("<nobr>Customer ID:</nobr>");
要关闭 HTML 标记,请调用 RenderEndTag。当您执行上述操作时,HtmlTextWriter 会自动创建一个与最近的开始标记相匹配的结束标记。您无需指定结束标记的类型,但是添加一个使代码更清楚的注释会有所帮助:
output.RenderEndTag(); //td
在需要呈现某个子控件时,应调用该控件的 RenderControl 方法并将 HtmlTextWriter 对象传递给它。由于 RenderControl 方法是 System.Web.UI.Control 基类的方法,所以它可用于所有的控件。下面的代码可呈现表单元格中的下拉列表:
output.AddAttribute("width", "200px"); output.RenderBeginTag("td"); this.CustomerIdChooser.RenderControl(output); output.RenderEndTag(); //td
RenderWebPart 方法中的其余代码调用要显示的标签控件的 RenderControl 方法,RenderControl 方法可添加用于将控件嵌入表单元格的标记。如果 ErrorLabel 控件可见,则唯一运行的呈现代码就是显示错误消息(此消息被指定给该控件的文本属性)的代码:
else // (ErrorLabel.Visible == true) { ErrorLabel.RenderControl(output); } }
如果您熟悉 ASP.NET 服务器控件和 HTML 的处理,就会发现为 Web 部件编写呈现代码非常简单。您只需将对 HtmlTextWriter 对象方法的调用与对子控件 RenderControl 方法的调用组合在一起。请记住替代 CreateChildControls,以便创建控件并将它们添加到 Controls 集合中。若您需要在 CreateChildControls 以外的方法中引用子控件,请首先调用 EnsureChildControls。
创建和显示自定义属性
当用户选择修改您的 Web 部件时,可以在浏览器任务窗格中方便地将您为 Web 部件类创建的任何自定义属性公开给用户。
您可以将几个特性添加到代码的属性定义中,以便调整如何将属性提供给用户。CustomerRowProvider Web 部件有一个名为 DetailViewEnabled 的属性,该属性决定显示多少数据。RenderWebPart 方法中用来呈现标签控件的部分检查 DetailViewEnabled 属性,并在 DetailViewEnabled 为真时呈现所有的标签。
添加到如下代码示例的 DetailViewEnabled 属性的特性为该属性指定了一个说明、一个自定义类别、一个默认值和一个友好名称。另外,如果将 WebPartStorage 特性设置为 Storage.Personal,则指定可以针对不同的用户存储该属性的不同值。图 2 显示了此代码在任务窗格的用户界面中的效果。
const bool DetailViewEnabled_Default = false; private bool _detailViewEnabled = DetailViewEnabled_Default; [Description("Enable display of details about this customer."), Category("View"), DefaultValue(DetailViewEnabled_Default), FriendlyName("Enable detailed view")] [WebPartStorage(Storage.Personal)] public bool DetailViewEnabled { get { return _detailViewEnabled; } set { _detailViewEnabled = value; } }
为了确保该属性显示在任务窗格中,您必须做的全部工作就是定义它,如上面的示例代码所示。但是,您可以替代 GetToolParts 方法以自定义属性的显示方式。此方法可返回要在任务窗格中显示的 ToolPart 对象的数组。
下面的代码首先将 CustomPropertyToolPart 对象添加到此方法返回的 ToolPart 数组中。即使没有此代码,也会显示包含自定义属性的自定义视图类别。但是,在默认情况下,自定义属性显示在标准属性后面。要将自定义属性移到任务窗格的顶部,请将它移到数组的第一个位置。CustomPropertyToolPart 对象自动显示所有自定义属性,WebPartToolPart 对象自动显示标准属性。此代码还指定应展开自定义视图类别。在默认情况下,其他类别在任务窗格打开时处于折叠状态。
public override ToolPart[] GetToolParts() { ToolPart[] toolparts = new ToolPart[2]; CustomPropertyToolPart cp = new CustomPropertyToolPart(); cp.Expand("View"); toolparts[0] = cp; toolparts[1] = new WebPartToolPart(); return toolparts; }
在这些示例 Web 部件中,可自动创建标准控件以显示自定义属性 #151; 一个复选框(用于 Customer Web 部件中的 DetailViewEnabled 布尔属性)和一个文本框(用于 Orders Web 部件中的 CustomerID 字符串属性)。但是,您可以使用 Web 部件基础结构,通过创建从 Microsoft.SharePoint.WebPartPages.ToolPart 类继承的类来为自己的属性创建自定义用户界面。Visual Studio .NET 的 Web 部件模板包括一个用来创建自定义 ToolPart 类的模板。
创建可连接的 Web 部件
Web 部件基础结构的一个非常诱人的功能就是它支持 Web 部件之间通信的方式。其他控件容器(可能从最初的 Visual Basic“Ruby”窗体包开始)已经使用事件驱动的模型来支持控件之间的交互。但是,在所有这些窗体包(包括 ASP.NET 中的页面框架)中,控件会引发某些事件,这些事件由容器中运行的代码处理。例如,Web 窗体或 Windows 窗体可能包括一个用于按钮单击事件的处理程序,该事件处理程序可以在窗体上的另一个控件中设置属性,并且有可能触发其他事件。但是,一个控件不能直接处理由另一个控件激发的事件。
Web 部件之间的通信模型有所不同。事件不是由页面、甚至是包含 Web 部件的 Web 部件区域来处理,而是由页面上其他 Web 部件中的处理程序来处理。Web 部件对于它们正与其他哪些 Web 部件进行通信没有硬编码知识,这将产生进一步的挑战。Web 部件之间的连接由用户在运行时实时创建。
这些挑战由 Web 部件连接基础结构来解决,该基础结构通过针对页面上每个已连接的 Web 部件调用 WebPart 基类的标准方法来调解 Web 部件之间的通信。要参与 Web 部件之间的通信,您的 Web 部件必须替代这些方法。
另外,可连接的 Web 部件必须实现本文前面描述的其中一个标准连接接口。Web 部件只能与其他可实现补充其自身的接口的 Web 部件进行通信,尽管基础结构能够提供可充当某些接口之间适配器的转换器。例如,该示例使用一个转换器将 IRowProvider Web 部件连接到 ICellConsumer Web 部件。在与 IRowProvider 通信时,该转换器充当 IRowConsumer,在与 ICellConsumer 通信时,该转换器充当 ICellProvider。
Web 部件基础结构调用 Web 部件的方法,来发现每页上可连接的 Web 部件。为了使连接选项可供用户使用,它将填充已添加到每个可连接 Web 部件中的菜单。
大多数连接接口都要求您声明事件委托。当用户创建连接时,Web 部件基础结构会将这些事件挂钩到其他 Web 部件中的处理程序。在构造页面时,该基础结构会针对每个可连接的 Web 部件调用一系列方法,因此,Web 部件可以通过激发由它们所连接的 Web 部件处理的事件来互相通信。
实现 IRowProvider
要创建 IRowProvider Web 部件,您必须声明两个事件并替代六个方法。以下各个部分介绍了这些任务,并提供了 CustomerRowProvider Web 部件中的示例代码。
声明 IRowProvider 事件
CustomerRowProvider 代码声明实现 IRowProvider 接口所必需的两个事件 — RowProviderInit 和 RowReady 事件。下面的代码显示这些事件的声明:
public event RowProviderInitEventHandler RowProviderInit; public event RowReadyEventHandler RowReady
在技术上,这两个事件声明就是这个 Web 部件类实现 IRowProvider 接口所必需的全部内容。因为该接口的互补接口 IRowConsumer 不引发任何事件,所以该接口不需要任何事件处理程序。
但是,要参与 Web 部件之间的通信,IRowProvider Web 部件还必须替代 WebPart 基类的六个方法,即使这些方法未包括在 IRowProvider 接口定义中也是如此。在这方面,Web 部件连接接口实际上是不仅仅需要实现所定义接口的设计模式。
替代 EnsureInterfaces
Web 部件基础结构调用某个 Web 部件的 EnsureInterfaces 方法,并期望该 Web 部件针对它的每个连接接口调用 WebPart 基类受保护的 RegisterInterface 方法。
RegisterInterface 方法包含八个参数,这些参数为 Web 部件基础结构提供协调与该 Web 部件的连接所需的信息。每个参数的用途在以下代码的注释中进行了描述:
public override void EnsureInterfaces() { /* Call RegisterInterface for each Web Part connection interface. Parameters: - InterfaceName: Name assigned to this interface. Use a unique name for each interface registered by this Web Part. - InterfaceType: The name of the interface type. Use constants provided by the InterfaceTypes class. - MaxConnections: The number allowed for this interface, either one or unlimited. - RunAtOptions: Where the connections can be implemented, on the server, the client, neither, or both. - InterfaceObject: A reference to this WebPart, the object implementing the interface. - InterfaceClientReference: For client-side connections, an identifier for the client-side object that implements this interface. Use the _WPQ_ token to generate a unique ID. Use String.Empty for server-side connections. - MenuLabel: Label for the interface that appears in the Connections submenu when a user is authoring connections. - Description: an extended explanation of the interface, used in some authoring environments. */ RegisterInterface( "RowProvider", // InterfaceName InterfaceTypes.IRowProvider, // InterfaceType WebPart.UnlimitedConnections, // MaxConnections ConnectionRunAt.Server, // RunAtOptions this, // InterfaceObject String.Empty, // InterfaceClientReference "Provide Customer Data to", // MenuLabel "Provides a row of data about the selected customer.");//Description }
替代 CanRunAt
Web 部件基础结构使用 CanRunAt 方法来确定 Web 部件是期望在客户端还是在服务器上实现其连接。这看上去可能是多余的,因为 RegisterInterface 方法的 RunAtOptions 参数能够解决同一个问题。然而,Web 部件可以使用此方法来提供用来执行以下操作的逻辑:基于在运行时可用的数据,计算预期的客户端或服务器设置。
Web 部件基础结构总是在某种程度上基于参与连接的其他 Web 部件的设置,针对在客户端还是在服务器上实现连接做出最终决策。
本文以及随附的示例没有演示如何创建客户端连接,因为这需要 Web 部件发出用来实现连接的脚本代码。
public override ConnectionRunAt CanRunAt() { return ConnectionRunAt.Server; }
替代 PartCommunicationConnect
在某个 Web 部件成功连接到另一个 Web 部件之后,Web 部件基础结构会调用此方法。它向该 Web 部件传递参数,指定连接了它的哪些接口、哪个 Web 部件和接口位于连接的另一端以及连接是在客户端上还是服务器上运行。在本示例中,此方法仅调用 EnsureChildControls 方法。此方法的一个可能用法是,提供仅在该 Web 部件连接到特定 Web 部件时才运行的代码。如果您创建了一组可互相利用各自特殊知识的 Web 部件,则您的代码就会在此处发现这些 Web 部件已建立连接。
public override void PartCommunicationConnect( string interfaceName, WebPart connectedPart, string connectedInterfaceName, ConnectionRunAt runAt) { EnsureChildControls(); }
替代 PartCommunicationInit
您必须声明 RowProviderInit 事件,但是在技术上,您不必为了满足 IRowProvider 接口的要求而引发该事件。但是,如果您在不引发 RowProviderInit 事件的情况下连接到某些使用者 Web 部件,则这些部件将无法正常工作,因此必须声明该事件。在替代 PartCommunicationInit 方法时需要引发该事件。该事件允许代码将初始化数据发送到您的 Web 部件所连接的 Web 部件。
示例 CustomerRowProvider Web 部件显示如何使用 RowProviderInit 事件的 RowProviderInitArgs 参数来发布其字段的名称以及要为每个字段显示的名称。用来处理该事件的使用者 Web 部件可以在响应中自定义自身,这可能是通过显示数据的标签来完成的。
有几个 Web 部件通信接口都包括一个 Init 事件(CellProviderInit、CellConsumerInit 等),该事件具有一个特定于该接口的 InitEventArgs 参数。RowProviderInitEventArgs 对象具有用来存储字符串数组的 FieldList 和 FieldDisplayList 属性。
本示例中的 CustomerRowProvider 类有两个专用字符串数组字段:_fieldList 和 _fieldDisplayList。这些数组在 CreateChildControls 方法中进行填充,并在此处用于设置 RowProviderInitEventArgs 对象的 FieldList 和 FieldDisplayList 属性。随后,代码引发 RowProviderInit 事件,将此数据发送到任何已连接 Web 部件的事件处理程序。
public override void PartCommunicationInit() { // Raise event if a handler has been assigned to the delegate. if (RowProviderInit != null) { RowProviderInitEventArgs rowProviderInitEventArgs = new RowProviderInitEventArgs(); rowProviderInitEventArgs.FieldList = _fieldList; rowProviderInitEventArgs.FieldDisplayList = _fieldDisplayList; RowProviderInit(this, rowProviderInitEventArgs); } }
替代 GetInitEventArgs
对于可以使用转换器的接口,您只需替代此方法。Web 部件基础结构在创建图 4 所示的转换器对话时调用此方法。此方法返回的数据用于自定义该对话框的用户界面。
此方法有一个用来标识所查询接口的参数,它返回一个从 InitEventArgs 派生的对象。此方法所返回对象的确切类型视接口而异。CustomerRowProvider Web 部件中的代码可确定是否正在查询此部件的 RowProvider 接口,如果正在查询的话,此代码将返回一个 RowProviderEventArgs 对象,该对象类似于在引发 RowProviderInit 事件时使用的对象。
public override InitEventArgs GetInitEventArgs(string interfaceName) { if (interfaceName == "RowProvider") { EnsureChildControls(); RowProviderInitEventArgs rowProviderInitEventArgs = new RowProviderInitEventArgs(); rowProviderInitEventArgs.FieldList = _fieldList; rowProviderInitEventArgs.FieldDisplayList = _fieldDisplayList; return(rowProviderInitEventArgs); } else { return(null); } }
替代 PartCommunicationMain
替代 PartCommunicationMain 方法可以引发任何其余的事件。在通过引发事件向所连接的使用者发送数据的提供者 Web 部件中,这通常是必需的。
IRowProvider Web 部件使用此方法来引发 RowReady 事件。RowReadyEventArgs 对象的 Rows 属性可存储 ADO.NET DataRow 对象的数组。
CustomerRowProvider Web 部件用单个 DataRow 对象填充 Rows 数组,该对象与在它的下拉列表中选择的客户 ID 相匹配。此类有一个受保护的 CurrentRow 字段,该字段在下拉列表的 SelectedIndexChanged 事件处理程序中进行填充。
除了 Rows 属性以外,RowReadyEventArgs 还有一个用来指出当前选择状态的字符串属性。此属性的可用字符串在嵌入以下代码的注释中进行了描述。
public override void PartCommunicationMain() { // Raise the event if a handler has been assigned to the delegate. if (RowReady != null) { RowReadyEventArgs rowReadyEventArgs = new RowReadyEventArgs(); rowReadyEventArgs.Rows = new DataRow[1]{CurrentRow}; // Set SelectionStatus to one // of these case sensitive strings: // "New" -- For grids that have a "New" (star) row. // "None" -- No row is selected. // "Standard" -- Normal selection of row or rows. rowReadyEventArgs.SelectionStatus = "Standard"; RowReady(this, rowReadyEventArgs); } }
提供者接口支持要在 PartCommunicationMain 中引发的各个事件。例如,ICellProvider Web 部件引发 CellReady 事件,IListProvider Web 部件引发 ListReady 事件。您的代码必须引发这些事件,即使当前的请求是回发请求并且您提供的数据未发生任何更改。某些接口提供特殊的事件来解决无法提供任何可用数据的情形 — 例如,NoFilter、NoParametersIn 和 NoParametersOut。如果无法引发这些事件,可能会导致使用者 Web 部件处于等待处理事件的状态,从而产生问题。
创建事件处理程序
实现连接接口的最后一步是为互补接口的事件创建任何必需的事件处理程序。然而,IRowConsumer 接口不包括任何事件,因此在实现 IRowProvider 时不需要任何处理程序。
实现 ICellConsumer
本示例中的 OrdersCellConsumer Web 部件举例说明了如何实现 ICellConsumer 接口。
ICellConsumer 的实现步骤与前面描述的 IRowProvider 的实现步骤非常相似:声明事件、替代方法和处理互补接口(在本例中为 ICellProvider)的事件。
ICellConsumer 接口只需要声明一个事件 — CellConsumerInit,该事件必须由 PartCommunicationInit 方法引发。
另外,ICellConsumer Web 部件必须:
• | 替代 EnsureInterfaces 以调用 RegisterInterface |
• | 替代 CanRunAt 以指定客户端或服务器端连接 |
• | 替代 PartCommunicationConnect 以添加针对连接而做出响应的代码 |
• | 替代 PartCommunicationInit 以引发 CellConsumerInit 事件,该事件向提供者 Web 部件发送一个 CellConsumerInitEventArgs 对象,此对象包含所使用单元格的字段名和显示名称的属性 |
• | 替代 GetInitEventArgs 以支持转换器 |
• | (可选)处理 ICellProvider 的 CellProviderInit 事件,以便从它的连接伙伴那里获得 FieldName 和 FieldDisplayName |
• | 处理 ICellProvider 的 CellReady 事件,以便从 CellReadyEventArgs 对象的 Cell 属性获取数据 注ICellConsumer Web 部件不需要替代 PartCommunicationMain,这是因为它们不引发任何其他事件。 |
安装细节
在后台,Web 部件受到丰富管理基础结构的支持。本文前面描述的 Stsadm.exe 工具可为它所安装的每个 Web 部件程序集执行下列操作:
• | 将 Web 部件程序集复制到 SharePoint 站点的 Bin 目录中。在本示例中,此程序集名为 Northwind.dll。 |
• | 将该程序集中每个 Web 部件的 .dwp 文件复制到此站点的 Wpcatalog 目录中。.dwp 文件是一个包含 Web 部件相关信息的 XML 文件,本文稍后将更详细地介绍该文件。本示例中包含两个 .dwp 文件:Northwind_CustomerRowProvider.dwp 和 Northwind_OrdersCellConsumer.dwp。所有在 Wpcatalog 目录中包含 .dwp 文件的 Web 部件都自动包括在虚拟服务器库中,虚拟服务器库是可在该服务器上使用的 Web 部件的目录。 |
• | 将所有 Web 部件资源文件复制到它在此站点的 Wpresources 目录中为该程序集创建的子目录中。本示例包含两个资源文件:Customers.xml 和 Orders.xml,用于存储由 Web 部件使用的数据。 |
• | 向该站点的 Web.config 文件的 SafeControls 部分中添加项目。该站点允许用户只加载那些在配置文件中列为安全的 Web 部件。 |
• | 将 .cab 文件复制到运行 Windows SharePoint Services 的服务器的配置数据库中,以便您可以通过按名称引用某个程序集,将该程序集安装到其他 SharePoint 站点上或者服务器场中的其他服务器上,而无需访问初始的 .cab 文件。 |
为了允许 Stsadm.exe 工具执行这些任务,.cab 文件必须包含该程序集、一个 .dwp 文件以及所有必需的资源文件。另外,.cab 文件必须包含一个名为 Manifest.xml 的 XML 文件,该文件用于指定每个程序集的 .dwp 和资源文件的名称,以及要添加到 Web.config 文件中的 SafeControls 项。下面是本示例中包括的 Manifest.xml 文件的完整文本:
<?xml version="1.0"?> <!-- You need to have just one manifest per Cab project for Web Part Deployment.--> <!-- This manifest file can have multiple assembly nodes.--> <WebPartManifest xmlns="http://schemas.microsoft.com/WebPart/v2/Manifest"> <Assemblies> <Assembly FileName="Northwind.dll"> <ClassResources> <ClassResource FileName="Customers.XML"/> <ClassResource FileName="Orders.XML"/> </ClassResources> <SafeControls> <SafeControl Namespace="Northwind" TypeName="*" /> </SafeControls> </Assembly> </Assemblies> <DwpFiles> <DwpFile FileName="CustomerRowProvider.dwp"/> <DwpFile FileName="OrdersCellConsumer.dwp"/> </DwpFiles> </WebPartManifest>
通过运行其他 Stsadm.exe 命令和命令行开关,可以从服务器中删除 Web 部件包、枚举所安装的软件包、指定应为特定虚拟服务器安装的软件包,或者指定应将程序集安装到全局程序集缓存 (GAC)(而非 Bin 文件夹)中。
要点 要将某个 Web 部件程序集安装到 GAC,您必须赋予该程序集一个强名称。Northwind 示例程序集没有强名称,因此它不能安装到 GAC 中。要赋予该程序集一个强名称,请编辑 AssemblyInfo.cs 文件中的 AssemblyKeyFile 属性,使其包含密钥对所在文件的路径,然后重新编译该项目。有关如何创建密钥对文件的信息,请参阅 Creating a Key Pair。在编译时使用密钥对文件签署程序集,可确保只有能够访问密钥对文件中私钥的开发人员才能对程序集进行更改。
有关如何创建 Web 部件的 .cab 文件以及如何使用 Stsadm.exe 工具可用选项的详细说明,请参阅打包和部署 Microsoft Windows SharePoint Services 的 Web 部件。
创建和部署 .dwp 文件
Windows SharePoint Services 使用扩展名为 .dwp 的 XML 文档来维护 Web 部件基于文件的持久性。这些文件可捕获 Web 部件属性设置的快照,以及对用于创建 Web 部件的程序集和类的引用。
在以前版本的 Web 部件技术中,实际代码中包含的 .dwp 文件用于实现 Web 部件的逻辑。在当前版本中,Web 部件逻辑在托管程序集中已编译的 ASP.NET 类中实现,.dwp 文档包含每个 Web 部件的程序集和类的名称。
在默认情况下,由 Visual Studio .NET 模板创建的 .dwp 文件只指定 Title 属性和 Description 属性的值,当用户向页面中添加 Web 部件时,这些值用于标识这些部件。但是,您可以向该 .dwp 文件中添加元素以指定其他属性值,替代在 Web 部件代码中实现的默认属性设置。
下面的示例显示包括在示例 Northwind 项目中的 Northwind_OrdersCellConsumer.dwp 文件的 XML 文本:
<?xml version="1.0" encoding="utf-8"?> <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2" > <Title>Orders (Cell Consumer)</Title> <Description>Northwind Orders Web Part: consumes a cell of data containing CustomerID, and displays the orders for that customer.</Description> <Assembly>Northwind</Assembly> <TypeName>Northwind.OrdersCellConsumer</TypeName> <!-- Specify default values for any additional base class or custom properties here. --> </WebPart>
此示例 .dwp 文件的内容具有 XML 的自编文档特性,因此易于理解和编辑。请注意,Web 部件的类名在 TypeName 元素中指定,并使用包括命名空间的名称。
要指定其他任何属性值,请添加一个标记,并使该标记所基于的属性名和文本包含所需的属性值。例如,您可以向示例 .dwp 文件中添加下行:
<CustomerId xmlns="Northwind">ALFKI</CustomerId>
在默认情况下,使用此版本 .dwp 文件安装的 Web 部件将显示客户 ALFKI 的定单。
在使用任务窗格修改了 Web 部件的共享属性和个人属性之后,您可以通过从 Web 部件菜单中选择“导出”,将 Web 部件的当前状态捕获到一个 .dwp 文件中。导入该 .dwp 文件会创建一个 Web 部件,其状态与以前导出的 Web 部件的状态相同。您可以使用 .dwp 文件来序列化和反序列化 Web 部件实例。这在使用某些 Web 部件(如用作静态 HTML 容器的内置内容编辑器 Web 部件)时尤其有用。
如果您能够找到某个 Web 部件的 .dwp 文件,而且该 Web 部件的程序集位于全局程序集缓存或者虚拟服务器的 Bin 目录中,则可以向页面中添加该 Web 部件,即使该 Web 部件尚未包括在库中也是如此。另外,该 Web 部件必须在虚拟服务器的 Web.config 文件中标识为安全,如本文的下一部分所述。
通过引用 .dwp 文件来添加 Web 部件
1. | 在 Web 部件页上,单击“修改我的网页”或“修改共享网页”。 |
2. | 指向“添加 Web 部件”,然后单击“导入”。 |
3. | 在任务窗格中,键入该 .dwp 文件的路径,或者单击“浏览”按钮,然后浏览至该 .dwp 文件所在的位置。所选 Web 部件即会出现在任务窗格中。 |
4. | 将该 Web 部件拖到页面上的 Web 部件区域中,或者使用“添加到”菜单选择一个 Web 部件区域,然后单击“添加”。 警告 在向站点库中添加 Web 部件时,会将 .dwp 文件复制到 Windows SharePoint Services 为该站点维护的内容数据库中。如果您对 .dwp 文件进行更改,则必须再次导入它,否则所作的更改将被忽略。 |
在您安装某个 Web 部件时,还可以让 Windows SharePoint Services 自动为该 Web 部件的程序集创建默认的 .dwp 文件。要进行此操作,请执行以下步骤:
1. | 在主页上,单击“网站设置”。 |
2. | 在“网站设置”页的“管理”区域中,单击“转到站点管理”。 |
3. | 在“顶级站点管理”页的“站点集合目录”区域中,单击“管理 Web 部件库”。 注 Microsoft Windows SharePoint Services 2.0 Beta 2 以前使用目录 这一术语,在本文中,此术语已更新为库 以反映当前的用法。 |
4. | 在“Web 部件库”页上,单击“新建 Web 部件”。 |
5. | 在“Web 部件库上:在“新建 Web 部件”页上,选中要添加到站点库中的任一 Web 部件复选框。所有这些 Web 部件在适用 Web.config 文件的 SafeControls 部分中都标识为安全。 |
6. | (可选)键入每个选定 Web 部件的 .dwp 文件的名称,系统会基于类名自动输入默认的文件名。 |
7. | 单击“填充库”。系统会自动创建一个最简单的 .dwp 文件,并将其添加到一个名为 Site_Name 的库中。 |
添加到 Site_Name 库中的 Web 部件即可供该站点及其下面的所有站点使用。
指定安全的 Web 部件
如果允许用户、甚至是 Administrator 站点组中的成员不受限制地导入新 Web 部件,可能会使服务器受到安全威胁。因此,必须先将 Web 部件显式指定为安全,才能在虚拟服务器上使用它们。这是通过在虚拟服务器的 Web.config 文件的 SafeControls 部分中创建项目来完成的。Web.config 文件通常位于以下位置:
system_drive:/Inetpub/wwwroot/Web.config
每个 SafeControls 项都标识一个包含一个或多个 Web 部件的程序集。您可以分别列出 Web 部件类,也可以指定程序集中的所有 Web 部件都是安全的。下面是一个 Web.config 文件的 SafeControls 部分的示例,它的前面有一些标记,显示它如何适应该文件的 XML 层次结构:
<configuration> <SharePoint> <SafeControls> <SafeControl Assembly="System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Namespace="System.Web.UI.WebControls" TypeName="*" Safe="True" /> <SafeControl Assembly="System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Namespace="System.Web.UI.HtmlControls" TypeName="*" Safe="True" /> <SafeControl Assembly="Microsoft.SharePoint, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint" TypeName="*" Safe="True" /> <SafeControl Assembly="Microsoft.SharePoint, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.WebPartPages" TypeName="*" Safe="True" /> <SafeControl Assembly="Northwind, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Namespace="Northwind" TypeName="*" Safe="True" /> </SafeControls>
每个 SafeControl 元素都有四个特性:
• | Assembly 程序集的名称,其中包含一个或多个 Web 部件。对于具有强名称的程序集,您必须包括名称、版本、区域性和公钥标记。对于其他程序集,尽管可以包括所有这四个部分,但只有名称是必需的。 警告 如果您通过在项目的 Assemblyinfo.cs 文件中添加密钥对文件的路径来为示例 Northwind 程序集赋予强名称,则必须向该程序集的 SafeControl 项添加公钥标记。要获得具有强名称的程序集的公钥标记,一个简单的方法是通过将 .dll 文件拖到 local_drive:/Windows/Assembly 目录中,以将该程序集添加到 GAC 中。然后,您可以右键单击该程序集并单击“属性”,以查看该程序集的属性。公钥标记即会显示出来,该标记视图非常方便,这是因为您可以选择它并将它复制到剪贴板上。如果您想将程序集从 GAC 中删除,请右键单击该程序集,然后单击“删除”。 |
• | Namespace Web 部件类的 .NET 命名空间。请注意,嵌套命名空间中的 Web 部件必须分别列出,即使针对 TypeName 输入星号时也是如此。例如,前面显示的 SafeControls XML 部分包括 Microsoft.SharePoint 程序集的两个单独的项目 — 一个用于 Microsoft.SharePoint 命名空间,另一个用于 Microsoft.SharePoint.WebPartPages 命名空间。 |
• | TypeName Web 部件的类名。您可以使用星号 (*) 来指出某个项目适用于所指定程序集和命名空间中的全部 Web 部件类。在开发程序集时,这尤其方便,因为在添加新的 Web 部件类并重新编译程序集时,无需进行任何更改。 |
• | Safe 通常,此特性的值为 True。但是,Administrator 站点组的成员可以通过将此特性设置为 False,来否认 Web 部件的安全并使其不可用。 |
SafeControls 列表可防止恶意 Web 部件供用户使用。另外,Web 部件受制于那些应用于所有托管代码的相同代码访问安全性控件。
代码访问安全性
在公共语言运行库为 Web 部件提供的各种有价值的服务中,有一项服务是代码访问安全性。根据本地计算机 Administrator 帐户成员控制的策略,可以防止 Web 部件代码执行某些类型的操作(如读取或写入文件)。
由于 Microsoft 尽力提供可信赖的计算技术,Windows SharePoint Services 的默认安全设置具有很强的限制性。您可能需要具备一定的前瞻能力,以确保 Web 部件程序集获得它们所需的权限。
运行库对每个 .NET 程序集的各个证据类型进行评估。例如,强名称就是一种证据类型,就如程序集的位置一样 — 无论将它安装在 GAC 中还是 bin 文件夹中。运行库还可以对指定安全策略的配置文件进行评估。基于证据和策略,每个程序集都被赋予一组权限。
注 在默认情况下,Microsoft Windows SharePoint Services 2.0 Beta 2 包括了一个非常宽容的安全策略。许多在 Beta 2 中能够正常工作的 Web 部件,在以后的 Beta 版本或商用版本的 Microsoft Windows SharePoint Services 2.0 上运行时,可能会产生安全异常。例如,示例 Northwind Web 部件需要具备从 XML 文件读取数据的文件访问权限 — 在默认情况下,这些权限在 Beta 2 中可用,但在以后的版本中不可用。
以下部分介绍了几种方法,您可以使用这些方法,通过调整代码访问安全性的证据和策略,在 Web 部件中启用功能。有关代码访问安全性的信息,请参阅 Microsoft SharePoint Products and Technologies Software Development Kit (SDK)。
在 Web.config 文件中指定信任级别
SharePoint 站点的 Web.config 文件包含一个 trust 元素,此元素指定赋予该服务器上运行的 Web 部件的默认安全级别。此元素出现在配置文件的 System.Web 部分中:
<trust level="WSS_Minimal" originUrl="" />
对于 level 特性来说,在默认情况下可用的值为 Full、High、Medium、Low、Minimal、WSS_Medium 和 WSS_Minimal。在这些级别中,只有三个级别允许 Web 部件运行:Full、WSS_Medium 或 WSS_Minimal。其他信任级别适用于 ASP.NET,但是不包括 Web 部件所需的特定权限。
用来在 Web 部件中启用功能的一种简单方法是,升级 Web.config 文件中的信任级别,但是此方法具有潜在危险。例如,示例 Northwind Web 部件需要两种类型的文件 I/O(输入/输出)权限 — 它们需要调用 Page.Server.MapPath 以发现 XML 数据文件的文件系统路径,并且还需要读取这些文件。这些权限对于 WSS_Minimal 信任级别不可用,但是,如果您将值更改为 WSS_Medium 或 Full,它们将变得可用。
使用这种提升 Web 部件权限的方法是非常危险的,因为它会全局应用到整个 SharePoint 虚拟服务器中。更好的做法是使用一种允许您区分不同 Web 部件并根据需要应用不同信任级别的方法。但是,对于调试和开发工作,改变 Web.config 文件中的设置可能会非常有用。
为了确保您对 Web.config 信任级别设置进行的所有更改生效,请在保存所作的更改之后,从命令提示符下运行 Iisreset。
创建和编辑策略文件
Web.config 文件中的每个信任级别都与用来指定一组权限的策略配置文件相对应。例如,WSS_Minimal 信任级别的权限在名为 Wss_minimaltrust.config 的文件中指定。
您可以编辑这些文件以更改默认策略,也可以创建自己的策略文件并引用 Web.config 文件中的自定义策略。Web.config 文件中的 SecurityPolicy 部分列出可用的自定义信任级别以及它们所基于的文件。下面的代码显示一个 SharePoint 站点的 Web.config 文件中的默认列表:
<securityPolicy> <trustLevel name="WSS_Medium" policyFile="C:/Program Files/ Common Files/Microsoft Shared/Web Server Extensions/60/ config/wss_mediumtrust.config" /> <trustLevel name="WSS_Minimal" policyFile="C:/Program Files/ Common Files/Microsoft Shared/Web Server Extensions/60/ config/wss_minimaltrust.config" /> </securityPolicy>
您可以添加可引用自定义策略的信任级别元素。每个策略文件都包含权限类的列表、已命名权限集及每个集合的权限的列表、用以定义证据(程序集必须提供该证据,以便为其分配一个特定的权限集)的代码组列表。
有关为 Web 部件创建自定义策略文件的信息,请参阅 Microsoft SharePoint Products and Technologies Software Development Kit (SDK)。
您还可以编辑其中一个默认策略文件。例如,您可以修改 Wss_minimaltrust.config 文件,以便向 Northwind Web 部件授予必需的文件 I/O 权限,即使在服务器的信任级别设置为 WSS_Minimal 时也是如此。
要执行这种修改,需要以下三个步骤:
• | 指定任何必需的权限类。 |
• | 定义权限集。 |
• | 定义用来基于证据分配权限集的代码组。 |
首先,将以下代码行添加到服务器上 Wss_minimaltrust.config 文件的 SecurityClasses 部分:
<SecurityClass Name="FileIOPermission" Description="System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
接着,将下面的权限集添加到 NamedPermissionSets 部分。此权限集包括执行和连接 Web 部件所必需的权限,以及一个文件 I/O 权限,该 I/O 权限限制在读取和发现虚拟服务器的 Wwwroot 目录树中的文件路径。
<PermissionSet class="NamedPermissionSet" version="1" Name="NorthwindPermissionSet"> <IPermission class="AspNetHostingPermission" version="1" Level="Medium"/> <IPermission class="SecurityPermission" version="1" Flags="Execution"/> <IPermission class="WebPartPermission" version="1" Connections="True"/> <IPermission class="FileIOPermission" version="1" Read="$AppDir$" PathDiscovery="$AppDir$" /> </PermissionSet>
您必须对策略文件进行的最后一次修改是添加一个代码组,该代码组可为符合您指定的成员身份条件(证据)的程序集分配自定义权限集。在本例中,代码组中的成员身份取决于是否具有指定的强名称。这种方法能够将提升的权限安全地限制到单个程序集。
XML 的代码组块嵌套在类别中。每个代码组定义都包含两个元素 — 一个 CodeGroup 元素和一个 IMembershipCondition 元素。
查找最外面的 CodeGroup 元素,该元素的 class 特性的值为 FirstMatchCodeGroup。该元素的 PermissionSetName 特性设置为 Nothing,这是因为它是引用已命名权限集的其他代码组的容器。将以下代码行嵌套在外面的 FirstMatchCodeGroup 代码组内部:
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="NorthwindPermissionSet"> <IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="your public key"/> </CodeGroup>
示例 Northwind 项目包含一个名为 Wss_mimimaltrust.config 且包括这些更改的文件。
为了使其生效,还需要执行最后一步。您需要用实际的公钥值替换 IMembershipCondition 元素中的“your_public_key”标记。为此,请使用 Sn.exe 工具生成一个密钥对文件,按前面所述的那样在 Assemblyinfo.cs 文件的 AssemblyKeyFile 特性中引用该密钥对文件,然后重新编译 Northwind 项目以生成具有强名称的程序集。要获得强名称 blob,请从命令提示符下运行以下命令:
secutil.exe –hex –s Northwind.dll
此外,还要记住的是,如果您为 Web 部件程序集赋予了一个强名称,还应当像前面所述的那样,将公钥标记值添加到 Web.config 文件中该 Web 部件的 SafeControl 项中。
安装到 GAC 中以实现完全信任
如果您赋予 Web 部件程序集一个强名称,则还可以选择提升它的信任级别。您可以将程序集安装在全局程序集缓存中,以便它自动以完全信任的级别执行。
如果您检查 Windows SharePoint Services 的策略文件,其原因会变得很清楚。即使是 Wss_minimaltrust.config 文件也包括下面的代码组:
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust"> <IMembershipCondition class="UrlMembershipCondition" Url="$Gac$/*" version="1"/> </CodeGroup>
当您使用 Stsadm.exe 工具为具有强名称的程序集安装 Web 部件 .cab 文件时,可以使用 globalinstall 命令行开关将其安装到 GAC 中,如下所示:
Stsadm.exe –o addwppack –filename path_to_Web_Part.cab file -globalinstall
您还可以将具有强名称的 Web 部件程序集手动安装到 GAC 中,方法是将该 Web 部件的 .dll 文件拖到下面的特殊文件夹中:
local_drive:/Windows/Assembly
特殊的 Wpresources 文件夹位置用于安装到 GAC 中的所有 Web 部件:
local_drive:/Program Files/Common Files/Microsoft Shared/ Web Server Extensions/wpresources
使用基于 Wpresources 目录的 URL 有一个好处,那就是 Web 部件基础结构可自动在适当的位置查找文件,具体在哪个位置查找取决于 Web 部件是安装到虚拟服务器的 bin 目录还是安装到 GAC 中。
但是,依赖 GAC 是添加 Web 部件权限的另一种简单但具有潜在危险的示例方法,这是因为安装在 GAC 中的 Web 部件始终以完全信任的级别运行。更好的做法是只赋予代码其所需的权限。安装在 GAC 中的 Web 部件还会自动供该计算机上的所有虚拟服务器使用,而这可能是您不希望的。
当您将 Web 部件安装到 Bin 目录中时,可以将 Web 部件限制到单个虚拟服务器,并限制可由 Web 部件使用的权限。
注 为了便于限制权限,您还可以进一步采取另一个步骤,那就是向 Web 部件程序集添加可指定它们所需的确切权限的特性。这会向程序集中添加元数据,从而更易于发现所需的权限。下面的安全特性示例可防止在文件 I/O 权限不可用时加载程序集:
using System.Security.Permissions;
[Assembly:FileIOPermission(SecurityAction.RequestMinimum)]
调试 Web 部件
Visual Studio .NET 便于在执行页面期间逐句通过 Web 部件类中的代码。但是,要调试 Web 部件,您必须首先确保它安装在运行 Windows SharePoint Services 的服务器上,否则您将无法运行它。下列步骤中详述的操作已经在本文前面更详细地介绍过:
1. | 设置 Web 部件项目的“输出路径”属性,使其指向虚拟服务器的 Bin 目录。 | ||||||||
2. | 从 .cab 文件安装 Web 部件,或者遵循以下三个步骤。 | ||||||||
3. | 向虚拟服务器上 Web.config 文件的 SafeControls 部分中添加一项。 | ||||||||
4. | 为 Web 部件创建一个 .dwp 文件。 | ||||||||
5. | 将 Web 部件加载到 Site_Name 库中。要进行此操作,请执行以下步骤:
|
您可能希望在开始调试之前将 Web 部件添加到页面中,也可能希望调试在添加 Web 部件时运行的代码,这两种情况都有可能出现。
当您准备开始调试时,请返回到 Visual Studio .NET 并在 Web 部件的代码中设置一个或多个断点。用来设置中断的两个较好的候选方法是 CreateChildControls 方法和 RenderWebPart 方法。
如果您尚未调试过 ASP.NET 自定义控件,则后面的步骤可能是最棘手的:
1. | 在“调试”菜单上,单击“进程”。 |
2. | 在“进程”对话框中,确保“显示系统进程”和“显示所有会话中的进程”复选框均被选中。 |
3. | 选择 W3wp.exe 进程。 |
4. | 单击“附加”。 |
5. | 在“附加到进程”对话框中,选中“Common Language Runtime”作为要调试的程序类型,然后单击“确定”。 |
6. | 单击“关闭”。 |
7. | 在浏览器中运行您的 Web 部件;执行过程会在断点处中断。 |
8. | 使用标准的 Visual Studio 方法来调试代码,然后在准备终止会话时从“调试”菜单中选择“停止调试”。 警告 如果您在测试 Web 部件之后尝试编辑和重新编译代码,则可能会收到一则错误消息,指出 Visual Studio .NET 无法替换输出 .dll 文件。要解决此问题,请使用任务管理器来终止 W3wp.exe 进程,或者从命令行运行 iisreset。这将释放对 .dll 文件的锁定,以便您可以重新编译。 |
有关调试 Web 部件的详细信息,请参阅调试 Web 部件。
小结
对于使用 Windows SharePoint Services(目前只能通过 Microsoft Windows Server 2003 使用)的公司,Web 部件提供了极具竞争力的技术。Windows SharePoint Services 协作环境和 ASP.NET 可扩展模型组合在一起,最终为开发人员提供了一个将最初的数字仪表板承诺变为现实的平台。
对于用户,Web 部件提供了全新的自由度和能力来汇集个性化页面、工作区和门户。对于管理员,Web 部件提供了安全和可伸缩性。对于设计师,Web 部件基础结构为 Web 应用程序的表示层提供了基于接口的可插入组件所带来的好处。