共用方式為


A Developer's Introduction to Web Parts

Click here to download sample - IntroWebPartsCode.exe.

 

Andy Baron
MCW Technologies, LLC

May 2003

Applies to:
    Microsoft® Windows® SharePoint™ Services
    Microsoft Office SharePoint Portal Server 2003
    Microsoft Visual Studio® .NET
    Web Part infrastructure

Summary: Learn what Web Parts are and how to create them. Developers can build Web Parts as ASP.NET custom controls. Administrators can install Web Parts on any site based on Windows SharePoint Services. Users can add Web Parts to pages by dragging and dropping in a browser, and they can personalize them by setting properties. Web Parts can connect to other Web Parts using standard interfaces. (43 printed pages)

A sample Visual Studio .NET solution that contains two custom Web Parts written in C# accompanies this article. With the first Web Part, users can select a customer and view configurable information about the customer. With the second Web Part, users can view the orders for a single customer. A user can add these Web Parts to a Web Part Page and connect them to each other, so that the second Web Part displays orders for the customer selected in the first Web Part.

**Note   **This paper introduces Web Parts to developers. This is not an introduction to Windows SharePoint Services or SharePoint Portal Server. For more information see SharePoint Products and Technologies.

The information in this article also applies to Microsoft Office SharePoint Portal Server 2003, which is built on the Windows SharePoint Services platform. The code samples that accompany this article should work when loaded into sites created with SharePoint Portal Server.

Download the IntroWebPartsCode.exe from the Microsoft Downloads Center.

Contents

Background
Web Parts Infrastructure
Installing Web Parts
Adding Web Parts to a Web Part Page
Setting Web Part Properties
Connecting Web Parts
Using Web Part Templates in Visual Studio .NET
Setting the Output Path Property for a Web Part Project
Web Parts as ASP.NET Custom Controls
Creating Web Part Classes
Adding Child Controls to Web Parts
Rendering the HTML for a Web Part
Creating and Displaying Custom Properties
Creating Connectable Web Parts
Implementing IRowProvider
Implementing ICellConsumer
Installation Details
Creating and Deploying .dwp Files
Specifying Safe Web Parts
Code Access Security
Debugging Web Parts
Summary

Background

For several years, a powerful idea has been roaming the corridors of Microsoft® in search of enabling technologies. The idea is to empower information workers to create personalized user interfaces by simply dragging and dropping plug-and-play components. Non-programmers should be able to bring together the information they care about and customize its appearance. In one location, they should be able to assemble a graph that shows sales for their division, a local traffic monitor, a stock ticker, a news feed focused on selected topics, and perhaps a calendar that shows their daily appointments.

In its first incarnation, this vision was originally named the Digital Dashboard, and its first canvas was Microsoft Outlook. Eventually, the browser emerged as the preferred container for pluggable Web Parts, and the vision melded into the industry-wide trend toward developing customizable Web portals.

Early implementations of the Digital Dashboard and Web Parts were widely touted in keynote speeches and generated interest among forward-thinking developers. However, the early technology was awkward to implement, and performance, scalability, maintainability, localizability and security were all less than perfect.

Meanwhile, two Web technology initiatives at Microsoft were rapidly moving forward. ASP.NET was building upon the emerging Common Language Runtime to provide Web developers with an advanced framework for using object-oriented technology to create fast, robust sites. Equally important, the Microsoft SharePoint™ Products and Technologies group was developing a sophisticated architecture for building and maintaining collaborative browser-based workspaces.

ASP.NET and Microsoft Windows® SharePoint Services, which are bundled with Microsoft Windows Server 2003, together provide the platform for a completely new implementation of Web Parts, one that truly delivers on the original vision.

Web Parts Infrastructure

The new Web Parts infrastructure builds on ASP.NET by providing a .NET object model that contains classes that derive from and extend ASP.NET classes. Additional classes and database objects handle storage (in Microsoft SQL Server™ or MSDE) and site administration.

Web Parts are ASP.NET server controls. To create a new type of Web Part, you create an ASP.NET custom control. However, unlike standard ASP.NET controls, which are added to Web form pages by programmers at design time, Web Parts are intended to be added to Web Part Zones on Web Part Pages by users at run time.

Depending on which site groups users are assigned to, and depending on the rights assigned to those groups, users can have varying levels of freedom to modify Web Parts and Web Part Pages. They can make changes that apply to all the users of a shared page, or they can make changes that apply only when they view the page.

In many ways, Web Parts blur the traditional distinction between design time and run time. The run-time experience of a user working with Web Parts in a browser is similar to the design-time experience of a Microsoft Visual Basic® programmer adding controls to a form and setting their properties. Web page designers can also build Web Part Pages in Microsoft Office FrontPage® 2003, which is able to render Web Parts in the design environment.

Web Parts rely heavily on Windows SharePoint Services to support:

  • Creation of new sites and new pages

  • Management of the user roster for a site

  • Storage of Web Part customizations, including shared and personal property settings

  • Administration of site backups and storage limits

  • A scalable architecture that can handle thousands of sites and millions of users

  • Assignment of users to customizable site groups

    Note   Microsoft SharePoint Products and Technologies no longer rely on role-based security for assigning rights and permissions to users. Instead, SharePoint Products and Technologies use site groups and cross-site groups to assign rights and permissions to users. Site groups are custom security groups that apply to a specific Web site. Cross-site groups are custom security groups that apply to more than one Web site. For more information, see Microsoft Windows SharePoint Services Help.

In turn, SharePoint Products and Technologies rely on Web Parts to provide configurable and extensible user interfaces.

Perhaps the most interesting and innovative new feature for Web Parts is an infrastructure in which Web Parts can use standard interfaces to communicate with each other. Users can use simple menu selections to connect Web Parts that are able to exchange data, and the Web Parts can be developed completely independently of each other. For example, a graphing Web Part from one vendor could be connected to a datasheet view Web Part from another, or a stock quote Web Part could show the current share price for the company selected in another Web Part that lists suppliers.

Installing Web Parts

This article is accompanied by a download that contains a solution created in Microsoft Visual Studio® .NET 2002. If you are using Visual Studio .NET 2003, you can open the solution and it will automatically be upgraded.

The sample solution, named Northwind, contains two projects. The first project, also named Northwind, outputs a Web control library named Northwind.dll, which includes two Web Part controls. The second project in the solution is a Cab project named NorthwindCab, which outputs a .cab file named NorthwindCab.cab. You can use this .cab file to install the sample Web Parts on a server that is running Microsoft Windows Server 2003 and Windows SharePoint Services.

A release version of the NorthwindCab.cab file is located in the \Northwind\NorthwindCab\Release directory in the sample files. To install the Web Parts from this .cab file, use the Stsadm.exe command-line tool located in the following directory on your server:

local_drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\bin

Log on with an account that has administrative rights on the server, open a command prompt, and then run the following command:

stsadm.exe –o addwppack –filename path_to_NorthwindCab.cab

CAUTION   You may need to adjust the security policy settings on your server running Windows SharePoint Services to allow the sample Northwind Web Parts to load data from XML files. For more information about how to do this, see the Code Access Security section later in this article.

Adding Web Parts to a Web Part Page

Windows SharePoint Services provides four types of galleries that can contain Web Parts:

  • Virtual Server gallery
  • <Site Name=> gallery
  • Web Part Page gallery
  • Online gallery

The Virtual Server Gallery lists Web Parts that are available to all sites on the server. The <Site_Name> Gallery contains Web Parts that are available to a particular site. By default, when you run Stsadm.exe to install a Web Part, Stsadm.exe adds the Web Part to the Virtual Server Gallery. More information about how to work with the administration tools available in Windows SharePoint Services to populate the <Site_Name> Web Site gallery is available later in this article.

A Web Part Page gallery contains Web Parts that are already added to the current page. This may seem paradoxical at first. Why would you need to add a Web Part that is already added to the page? The reason this is useful is that you can add a Web Part to a page and then close the Web Part. To close a Web Part, click the arrowhead on the right side of the Web Part title bar, and then click Close. A closed Web Part is no longer visible on the page, but it is still a member of the Web Part Page gallery for that page. The Web Part is still associated with the page by an entry in the configuration database of the server running Windows SharePoint Services, which also stores any shared or personalized property settings for the Web Part.

You can add a Web Part to a page, personalize it extensively, close it, and then later add it back to the page with the personalization intact. To bring back a closed Web Part, select it from the Web Part Page gallery.

Note   To truly delete a Web Part from a page, you must click the Modify My Page menu (or the Modify Shared Page menu in shared view), and then click Design this Page to add the Delete command to the drop-down menu of the Web Part. To remove the Web Part from the page without adding it to the Web Part Page gallery, click the arrowhead on the right side of the Web Part title bar, and then click Delete.

The online gallery is a set of Web Parts that are available over a Web service. This permits many servers to share access to a common, centrally maintained gallery of Web Parts. The URL for this Web service is specified in the OnlineLibrary element of the Web.config file for a site.

Important   To enable the online gallery, you must edit the Web.config file on the server and change the URL attribute of the OnlineLibrary element to the following:

<OnlineLibrary

Url="http://officebeta.microsoft.com/gallery/gallery.aspx"/>

Important   If you use a proxy server, you must also add the following section:

<system.net>

<defaultProxy>

<proxy proxyaddress="http://server_name:port_number" bypassonlocal =

"true"/>

</defaultProxy>

</system.net>

To add a Web Part that appears in any of the four default libraries to a SharePoint page, follow these steps:

  1. To add a Web Part only to your own version of the page, make sure that the Modify My Page menu appears at the top of the page. If the Modify Shared Page menu appears at the top of the page, click the Modify Shared Page menu, and then click Personal View.

    To add a Web Part to the page for all users, make sure that the Modify Shared Page menu appears at the top of the page. If the Modify My Page menu appears at the top of the page, click the Modify My Page menu, and then click Shared View.

  2. On the Modify My Page menu (Personal View) or the Modify Shared Page menu (Shared View), point to Add Web Parts, and then click Browse or Search.

    Note   If you click Browse, you can select any of the four libraries to view a list of the Web Parts in that gallery. If you click Search, you can limit your selection to Web Parts with names that contain your search text. Both the Browse command and the Search command open a list of the four available libraries, with a number for each gallery that shows how many Web Parts are available in that gallery. When you select a gallery, a list of the Web Parts in that gallery appears.

  3. Drag the Web Part that you want to add from the task pane into a Web Part Zone on the Web Part Page. You can then close the task pane or select more Web Parts.

Figure 1 shows the sample CustomerCellConsumer Web Part being dragged onto a page from the Virtual Server gallery. This is the gallery where the sample Web Parts are installed when you use the Stsadm.exe tool, as described earlier in this article.

Figure 1. Adding a Web Part to a page in Internet Explorer

Drag-and-drop operations for adding Web Parts to a page are available in browsers that support rich user interaction. However, Web Parts do not require Microsoft Internet Explorer or even a browser that supports dynamic HTML. That is one reason that the task pane shown in Figure 1 includes a drop-down list for choosing a Web Part Zone on the page and an Add button for adding the selected Web Part to the selected zone. Additionally, these alternative controls make Web Parts accessible to users who do not use a mouse.

Note   Several of the figures in this article show screenshots of Web Parts on the home page for a site. You are not limited to adding Web Parts to the home page for a site. To create a new Web Part page, click Create in the menu bar, scroll to the bottom of the Create Page page to the Web Pages heading, and then click Web Part Pages. On the New Web Part Page page, you can name the new page, you can select a page layout from a list, and you can select the document library to contain the new page. You can then add Web Parts to the new page.

Setting Web Part Properties

Web Parts share common properties, such as Title, Height and AllowClose. The Zone that a Web Part appears in is also a property of the Web Part. You can add custom properties to your Web Parts by adding standard .NET properties to the class for the Web Part.

When you set Web Part properties in the browser, the scope of the modification depends on whether the page is in Personal view or Shared view. Changes made in Personal view apply only to the current user and take precedence over changes made in Shared view. Use Shared view to set default values for all users of that page.

To change the properties for a Web Part, click the arrowhead on the right side of the Web Part title bar, and then click Modify My Web Part (in Personal view) or Modify Shared Web Part (in Shared view). You do not need to switch the page to Design view to make this selection. However, the page automatically changes to Design view when the task pane for setting properties appears. In Design view, you see an outline around each Web Part Zone and a title for the zone.

The sample CustomerRowProvider Web Part has a custom Boolean property, DetailViewEnabled, which determines how much information about the selected customer is displayed in the Web Part. Figure 2 shows how the property appears in a task pane in the browser.

Figure 2. Setting a custom property in Internet Explorer

Attributes in the C# code for the property (described later in this article) provide the custom View category for this property and the friendly name and tool tip that appears in the task pane. The Web Part infrastructure automatically creates a check box control for the property because it has a Boolean data type.

Connecting Web Parts

The Web Part infrastructure provides rich support for communication between Web Parts. Developers can use standard interfaces to create Web Parts that can exchange information with each other. For example, a Web Part that implements the ICellConsumer interface can receive information from a Web Part that implements the ICellProvider interface. Users can connect Web Parts to each other using simple menu commands in the browser.

Additionally, the Web Part infrastructure provides transformers that allow Web Parts to communicate with each other even if their interfaces are not exactly complementary. The Web Part infrastructure automatically detects an interface mismatch and displays a dialog box that allows the user to map values from one interface to the other. For example, if a user connects a Web Part that implements the IRowProvider interface to a Web Part that implements the ICellConsumer interface, a dialog box allows the user to select a single column of data from the IRowProvider Web Part to send to the ICellConsumer Web Part. The sample Northwind Web Parts provide an example of this.

To connect one CustomerRowProvider and one OrderCellConsumer Web Part after you add them to a page, follow these steps:

  1. If the page is not already in Design view, click the Modify My Page menu or the Modify Shared Page menu, and then click Design this Page.

    Note   You can create connections only when the page is in Design view.

  2. Click the arrowhead in the title bar of the Orders Web Part, point to Connections, point to Get Customer ID from (the label assigned to the only connection interface provided by this Web Part), and then click Customer (Row Provider), as shown in Figure 3. Note that several of the Web Parts on the page appear in this submenu, because they all implement connection interfaces that are compatible with the ICellConsumer interface of the Orders Web Part.

    Figure 3. Connecting Web Parts in Internet Explorer

  3. After you select the Web Part to connect with, a dialog box appears, as shown in Figure 4. No code was required to create this dialog box. It appears automatically to request the extra information required to connect an ICellConsumer Web Part to an IRowProvider Web Part.

    Figure 4. A dialog box requests information needed to create the connection.

  4. Select Customer ID (this is the default selection because it is first on the list), and then click Finish.

  5. To view the orders for a specific customer in the Orders Web Part, select a customer ID in the Customer Web Part, as shown in Figure 5.

    Figure 5. The Orders Web Part displays orders for the customer selected in the Customer Web Part.

You may wonder why the Customer Web Part did not implement the ICellProvider interface to provide a customer ID instead of requiring a transformer to connect to the Orders Web Part. By implementing the IRowProvider interface instead of the ICellProvider interface, the Customer Web Part is more versatile. It can connect with Web Parts that implement the IRowConsumer or IFilterConsumer interfaces, and it can connect with a variety of ICellConsumer Web Parts. For example, the Customer Web Part can provide data from its PostalCode column to an ICellConsumer Web Part that displays the weather for a specified postal code.

There are three pairs of interfaces that you can use to connect Web Parts in a browser:

  • ICellProvider/ICellConsumer   This interface pair communicates a single cell of data. Using a transformer, ICellConsumer can also connect with IRowProvider.
  • IRowProvider/IRowConsumer   This interface pair communicates a row of data. Using transformers, IRowProvider can connect with ICellConsumer and with IFilterConsumer.
  • IListProvider/IListConsumer   This interface pair communicates an entire list of data.
  • IFilterProvider/IFilterConsumer   This interface pair communicates a filter expression that contains one or more pairs of column names and values. IFilterConsumer can use a transformer to connect with IRowProvider, but this type of connection can only handle a single column name/value pair. In other words, you can only filter on a single column if you use a transformer to connect to IRowProvider.

Additionally, there are two interface pairs you can use to connect Web Parts in FrontPage 2003, but not in a browser. You can also use these interfaces to create cross-page connections, which use query strings to exchange data:

  • IParametersOutProvider/IParametersOutConsumer   An IParametersOutProvider Web Part defines a set of parameters that it can send to a compatible IParametersOutConsumer Web Part. Using a transformer, an IParametersOutProvider Web Part can also connect with an IParametersInConsumer Web Part.
  • IParametersInProvider/IParametersInConsumer   An IParametersInConsumer Web Part defines a set of parameters that it can receive from a compatible IParametersInProvider Web Part. Using transformers, an IParametersInConsumer Web Part can also connect with an IParametersOutProvider Web Part or with an IRowProvider Web Part.

The distinction between the IParametersOut and IParametersIn interface pairs may seem confusing. The important difference is that the provider defines the parameters for the IParametersOut pair of interfaces, and the consumer defines the parameters for the IParametersIn pair of interfaces. When a user tries to connect an IParametersOutProvider Web Part to an IParmetersInConsumer Web Part, a dialog box appears that the user can use to define how the parameters from the provider Web Part should map to the parameters of the consumer Web Part. These interfaces are very versatile, but remember that users must use FrontPage 2003 to connect these interfaces. They cannot connect these interfaces in the browser.

The following table shows the interface pairs that can be connected with one another through a transformer and whether the connections require FrontPage 2003.

Connectable through transformer Connectable in Browser Connectable in FrontPage
IRowProvider to ICellConsumer Yes Yes
IRowProvider to IFilterConsumer Yes Yes
IParametersOutProvider to IParametersInConsumer No Yes
IRowProvider to IParametersInConsumer No Yes

The following table shows the interfaces that can be connected across pages. To connect Web Parts on different pages, you must use FrontPage 2003.

Source Page Interface Target Page Interface
IRowProvider IFilterConsumer
IRowProvider IParametersInConsumer
IFilterProvider IFilterConsumer
IParametersOutProvider IParametersInConsumer
IParametersInProvider IParametersInConsumer

Using Web Part Templates in Visual Studio .NET

All Web Parts are ASP.NET custom controls, so Visual Studio .NET provides an excellent environment for creating and debugging Web Parts.

To create a project for your Web Part custom controls in Visual Studio, you can use the standard template for building a Web Control gallery or you can use a special template for building a Web Part gallery. The Web Part gallery templates are not included with Visual Studio .NET. They are available as free downloads from Web Part Templates for Microsoft Visual Studio .NET.

Web Part gallery templates are available for C# and for Visual Basic .NET. You select which languages you want to install when you run the setup program for the templates. To install the templates, you must also specify the path to the Microsoft.SharePoint.dll file. By default, the Microsoft.SharePoint.dll file is installed in the following location on a computer that is running Windows SharePoint Services:

local_drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\ISAPI

There are several advantages to using a Web Part gallery template instead of the standard Web Control gallery template. Projects generated from the Web Part gallery template automatically include the following useful items and capabilities:

  • A reference to the Microsoft.Sharepoint.dll file
  • A Web Part class file and a matching .dwp file (.dwp files are XML files that reference a Web Part's assembly, namespace, and class name, plus optional property settings for the Web Part)
  • The ability to add files that contain sample code for the following types of items:
    • A basic Web Part that inherits from Microsoft.SharePoint.WebPartPages.WebPart and overrides RenderWebPart
    • A consumer or provider Web Part with sample code that implements the ICellConsumer or ICellProvider interface, overrides the necessary methods, and handles the necessary events
    • A Tool Part class with sample code that inherits from Microsoft.SharePoint.WebPartPages.ToolPart and overrides the ApplyChanges, SyncChanges and CancelChanges methods
    • A Web Part .dwp file that contains sample entries for the required XML elements
    • A Web Part Manifest.xml file that contains sample entries for the required XML elements (Manifest.xml files are used by the Stsadm.exe tool during installation of Web Parts)

Setting the Output Path Property for a Web Part Project

When you create a Web Part in Visual Studio .NET, you must change the Output Path property to point to the bin folder of your Windows SharePoint Services site, even if you use a Web Part gallery template to create your project. You also need to make this change in the sample Northwind project that accompanies this article if you want to make changes to the project, recompile it, and test your changes. By default, the Output Path property points to the local Bin folder in the project directory.

To edit the Output Path property in Visual Studio .NET, follow these steps:

  1. In Solution Explorer, right-click the project, and then click Properties.
  2. In the left pane, click Configuration Properties, and then click Build.
  3. Under Outputs, click the Output Path property value, type the path to the Bin folder for your virtual server (or click the ellipsis (...) button to browse to the location of the Bin folder), and then click OK.

The path is converted from an absolute path to a relative path, as shown in Figure 6.

Figure 6. Edit the Output Path property to point to the Bin folder for your site.

When you recompile your project, the compiler automatically places the .dll file that contains your Web Part assembly in the directory specified by the Output Path property. If a file with the same name already exists in that directory, the new file replaces it.

CAUTION   If you recompile the sample Northwind project before you edit the Output Path property, the compiler creates a set of folders that match the relative path specified by the original property setting, which may not correspond to the location of the Bin directory for your SharePoint site.

Web Parts as ASP.NET Custom Controls

Creating Web Parts is a variation on the process of creating ASP.NET custom controls. For this reason, a quick review of the ASP.NET extensibility model will be helpful before delving into the details of how to create a Web Part.

ASP.NET provides two abstractions for extending the server control framework: user controls and custom controls.

User controls are essentially ASP.NET pages that can be inserted into other pages. They are in some ways analogous to the Include files that are used in classic ASP. Using Visual Studio .NET, you can easily build user controls by dragging server controls onto a user control designer, the same way you can drag controls onto a page designer. User controls create .ascx files that are very similar to the .aspx files created when building ASP.NET pages. However, you cannot create Web Parts by building user controls. To create a Web Part, you must create an ASP.NET custom control.

Unlike user controls, ASP.NET custom controls are not supported by the graphical tools in Visual Studio .NET. To build a custom control, you must create a class that inherits either directly or indirectly from System.Web.UI.Control, and you must write your own code to emit HTML. All the server controls included with ASP.NET were created this way. To help you create custom controls, the ASP.NET framework provides the HtmlTextWriter class. The HtmlTextWriter class has dozens of methods, properties, and companion enumerations that you can use to generate robust HTML easily instead of simply building up strings. The ASP.NET infrastructure coordinates the rendering of all the controls on a page.

When you create an ASP.NET custom control that inherits directly from the Control class, you generate HTML by overriding the base class Render method. This method takes an HtmlTextWriter object as a parameter. This object is passed to the method by the ASP.NET infrastructure at run time, and your code emits HTML by calling methods on that HtmlTextWriter object.

Note   To avoid confusion, remember that ASP.NET uses the word "render" differently from the common usage. Rendering usually means converting HTML text into a graphical display, a task that is most commonly performed by a browser. However, in ASP.NET, rendering means generating HTML in a server control.

The recommended practice for creating ASP.NET custom controls is not to inherit directly from the System.Web.UI.Control class. It is usually more efficient and more reliable to inherit from System.Web.UI.WebControls.WebControl, which itself inherits from the System.Web.UI.Control class. The WebControl class includes implementations of common style-related properties, such as BackColor, BorderWidth, Font and so on and it takes care of rendering those properties as HTML attributes.

When you create a class that derives from the WebControl class instead of directly from the Control class, you do not override the Render method. Instead, you override a method named RenderContents, which is a method of the WebControl class. The WebControl class overrides the Render method to create an outer tag for the control, with attributes that correspond to its style-related properties. Overriding the RenderContents method in your control allows you to emit HTML that is properly embedded within the tags created by the Render method for the control. The RenderContents method takes an HtmlTextWriter object as a parameter, just as Render does.

Developing Web Part custom controls follows a similar pattern. However, instead of creating a class that inherits from the WebControl class, you create a class that inherits from the Microsoft.SharePoint.WebPartPages.WebPart class. This WebPart class inherits from the System.Web.UI.Control class, just as WebControl does. The WebPart class takes care of creating the chrome around the control (for example, the title bar and border), which users can customize by setting properties and applying themes. The WebPart class also handles interactions with the WebPartPage and WebPartZone classes to support adding, moving, hiding, deleting, connecting, customizing, and personalizing Web Parts on a page. Figure 7 shows the WebPart class hierarchy and shows how Web Parts compare to other custom controls.

Figure 7. Web Parts are derived from the Microsoft.SharePoint.WebPartPages.WebPart class, which is derived from System.Web.UI.Control.

If you have ever created an ASP.NET custom control, then you will find the process of creating a Web Part very familiar. The techniques demonstrated in books, articles, and sample code that pertain to custom controls also apply to the task of building a Web Part custom control. Just remember to inherit from the WebPart class instead of from WebControl, and remember to override the RenderWebPart method instead of the RenderContents method.

In a standard custom control, you have the option of directly overriding the Render method if you really want to, even if you are inheriting from the WebControl class. However, in the WebPart class the Render method is sealed. In a class that derives from WebPart, you must override RenderWebPart instead of Render.

The Web Part infrastructure gives Web Parts several advantages over other custom ASP.NET controls:

  • Users can add Web Parts that are available in Web Part galleries to Web Part zones.
  • Users can modify personal or shared properties for a Web Part and make the changes persistent.
  • Web Parts can connect to each other using standard interfaces, and users can initiate these connections.

Web Parts participate in the same execution lifecycle as other ASP.NET server controls. For an overview of the phases in this lifecycle, with links to more detailed information, see Control Execution Lifecycle.

Creating Web Part Classes

The two sample Web Parts that accompany this article (CustomerRowProvider and OrdersCellConsumer) contain commented code that you can use as a guide to create your own Web Parts.

The first step in creating a Web Part control is to define a class that inherits from the WebPart class and that optionally implements one or more of the Web Part communication interfaces. The following code includes the class declaration for the CustomerRowProvider Web Part:

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
{

Three attributes are applied to the class. They are inserted automatically if you use either the Web Part or Web Control template in Visual Studio .NET. The first two attributes, DefaultProperty and ToolboxData, govern behavior in a design environment like Visual Studio—they specify the property that is selected by default in the property window and the text that is inserted into an ASPX or ASCX file if the control is dragged onto a page or a user control. The tag prefix that is registered for the control in the Page directive (or Control directive for a user control) replaces the {0} token.

This underscores the fact that Web Part controls can be dragged onto ASP.NET Web form pages just like any other server controls. However, using this method to embed a Web Part directly on an ASP.NET page is rarely a good idea, because you lose the functionality of the Web Part infrastructure—users can't save changes to personal or shared properties, and they can't connect Web Parts to each other. To get that functionality, a Web Part must be added to a Web Part zone, which is done in a browser or in Front Page, not in Visual Studio.

The XmlRoot attribute is also added by the templates—it comes into play when an object based on this class is serialized to XML.

The class definition for the OrdersCellConsumer class is similar to the preceding code sample, except that it implements ICellConsumer rather than IRowProvider.

Adding Child Controls to Web Parts

Even though all HTML rendering in a Web Part server control is performed by calling methods of the HtmlTextWriter object in RenderWebPart, you can still use ASP.NET controls inside your Web Part. These are referred to as child controls. You create variables for these child controls the same way you would in an ASP.NET page class.

The CustomerRowProvider code includes declarations for several ASP.NET child controls, a drop-down list, and ten labels:

 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;

To use these child controls in the Web Part, you must override the CreateChildControls method of System.Web.UI.Control. In this method, you instantiate the controls, set their properties, and hook up any event handlers. You also must add the controls to the Controls collection property of the Web Part, which is inherited from System.Web.UI.Control. The code begins by instantiating the ErrorLabel control, which is invisible by default. This control displays an error message if an exception occurs while attempting to load the data for the Web Part.

protected override void CreateChildControls()
 {
   ErrorLabel = new Label();
   ErrorLabel.Visible = false;
   Controls.Add(ErrorLabel);

Next, the code instantiates the drop-down list control, hooks up its event handlers, and adds this control to the Controls collection of the Web Part:

   CustomerIdChooser = new DropDownList();
   CustomerIdChooser.Load += new EventHandler(CustomerIdLoad);
   CustomerIdChooser.SelectedIndexChanged += 
    new EventHandler(CustomerIdChanged);
   Controls.Add(CustomerIdChooser);
   

Similar code is used to instantiate the remaining label controls, set their properties, and add them to the Controls collection.

In an ASP.NET Web form or user control, some control property settings can be specified as attributes in the .ASPX or .ASCX file. However, in a Web Part as in any custom control, all property settings must be specified in the code. This is similar to the pattern used in .NET Windows Forms, where all control property settings appear in the code and all controls are explicitly added to the Controls collection property of the form.

The CustomerRowProvider code contains two event handlers for the drop-down list—one to load the list and one to respond to selections. In the Load event handler, the code reads schema and data from an XML file into an ADO.NET DataSet. This code assumes that the XML file is located in the Wpresources directory. Every Web Part has access to this directory, so it is a good place to store images, localization resources, or other files that the Web Part needs. However, to access any file, even one that is stored in the Wpresources directory, your code must have adequate permissions. Various options for ensuring that adequate permissions are assigned to your Web Parts are discussed later in this article.

The ReadXml method requires a file path instead of a URL, so the code uses the Server.MapPath method to map the relative path of the Wpresources directory to the actual file path on the server. This code is wrapped in a try/catch block so it can respond gracefully if file I/O permissions are not available:

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;

The code needs to populate the drop-down list only when the page is first loaded. During a postback request, the list is loaded automatically from the ViewState property, as on any ASP.NET page. However, you cannot rely on checking the value of the IsPostBack property for the page the way you would if you were writing code in an ASP.NET page, or even in an ASP.NET custom control.

You do have access to the IsPostBack property, using the following: this.Page.IsPostBack. However, when a Web Part is added to a page, a postback request occurs, even though the ViewState data for the Web Part is not yet populated. This type of postback request never occurs for a server control that is directly embedded on an ASP.NET page, where postback requests only occur after the Web Part is already loaded once. This underscores a subtle difference between Web Parts and other custom controls: the user can add Web Parts to a page at runtime, and that action triggers a postback request.

Instead of checking the IsPostBack property, the following sample code checks whether the list already contains any items:

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

The code for the SelectedIndexChanged event for the list is typical of the code for any standard ASP.NET page, so it is not included here. The code populates the labels of the Web Part when a customer is selected.

Rendering the HTML for a Web Part

Using child controls in a Web Part is optional, but every Web Part that displays anything to the user must override the RenderWebPart method. This is where your Web Part generates the HTML that is rendered inside the Web Part frame.

If your Web Part uses child controls, you should begin the RenderWebPart method by calling EnsureChildControls. This method of System.Web.UI.Control automatically checks the ChildControlsCreated property. If that property is false, it calls CreateChildControls. EnsureChildControls is called at many points in the Web Part code as a safe way to make sure that the child controls are there when you need them without creating them more than once:

 protected override void RenderWebPart(HtmlTextWriter output)
 {
      EnsureChildControls();

The CustomerRowProvider Web Part contains a label named ErrorLabel that is hidden by default. It becomes visible only if an exception occurs when attempting to read the XML data file in the CustomerIdLoad event handler.

After making sure that any child controls have been created, the RenderWebPart code checks if ErrorLabel is still invisible, which indicates whether the data was successfully loaded:

if (ErrorLabel.Visible == false)
{

Writing code to emit HTML in RenderWebPart consists of calling methods of the HtmlTextWriter object that is passed to the RenderWebPart method. To create an HTML tag, first call the AddAttribute method to define any attributes that you want to add to the tag, then call the RenderBeginTag method and specify the HTML element that you want to create. When it is time to close a tag, call RenderEndTag to close the last tag that was opened.

The rendering code for CustomerRowProvider begins by creating a div tag that specifies 2 pixels of cell padding:

      output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "2px");
      output.RenderBeginTag("div");

A slightly more robust way of creating this tag is available. Instead of hard coding the name of the <div> tag, you can use the HtmlTextWriterTag enumeration:

      output.RenderBeginTag(HtmlTextWriterTag.Div);

Enumerated values are available for all the common HTML tags, and you can use them to provide compile-time protection against misspelling a tag.

If you need to set multiple attributes of an HTML tag, you can call AddAttribute several times before you call RenderBeginTag. All the specified attribute values are added to the beginning tag. The following code sets several attributes of a table tag:

      output.AddAttribute(HtmlTextWriterAttribute.Width, "400px");
      output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "2px");
      output.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "1px");
      output.RenderBeginTag("table");

When the Web Part is rendered in a browser, the preceding code creates the following HTML:

      <table width="400px" cellpadding="2px" cellspacing="1px">

You can also create CSS style tags that combine several settings in one attribute by calling AddStyleAttribute instead of AddAttribute:

      output.RenderBeginTag("tr");
      output.AddStyleAttribute(HtmlTextWriterStyle.FontWeight, "bold");
      output.AddStyleAttribute(HtmlTextWriterStyle.Width, "200px");
      output.RenderBeginTag("td");

The preceding lines of code emit the following HTML:

      <tr>
           <td style="font-weight:bold;width:200px;">

In addition, you can emit hard-coded HTML strings by calling the Write method of the HtmlTextWriter object:

      output.Write("<nobr>Customer ID:</nobr>");

To close an HTML tag, call RenderEndTag. When you do this, the HtmlTextWriter automatically creates an ending tag to match the most recent beginning tag. You do not need to specify the type of tag that you are ending, but it can be helpful to add a comment that makes the code more explicit:

      output.RenderEndTag(); //td

When it is time to render a child control, call the RenderControl method of the control and pass it the HtmlTextWriter object. The RenderControl method is available for all controls because it is a method of the System.Web.UI.Control base class. The following code renders the drop-down list in a table cell:

      output.AddAttribute("width", "200px");
      output.RenderBeginTag("td");
      this.CustomerIdChooser.RenderControl(output);
      output.RenderEndTag(); //td

The remaining code in the RenderWebPart method calls the RenderControl method of the label controls that you want to display, and it adds tags to embed the controls in table cells. If the ErrorLabel control is visible, then the only rendering code that runs is the code to display the error message that was assigned to the text property of this control:

else // (ErrorLabel.Visible == true)
   {
      ErrorLabel.RenderControl(output);
   }
}

If you are comfortable working with ASP.NET server controls and with HTML, you will find that writing the rendering code for Web Parts is very straightforward. Just combine calls to the methods of the HtmlTextWriter object with calls to the RenderControl method of your child controls. Remember to override CreateChildControls to create the controls and add them to the Controls collection. Whenever you need to refer to a child control in a method other than CreateChildControls, call EnsureChildControls first.

Creating and Displaying Custom Properties

Any custom properties that you create for your Web Part class can easily be exposed to users in the browser task pane when they choose to modify the Web Part.

There are several attributes that you can add to the property definition in your code to adjust how to present the property to the user. The CustomerRowProvider Web Part has a property named DetailViewEnabled that determines how much data is displayed. The section of the RenderWebPart method that renders the label controls examines the DetailViewEnabled property and renders all the labels if DetailViewEnabled is true.

The attributes added to the DetailViewEnabled property in the following code example specify a description, a custom category, a default value, and a friendly name for the property. Additionally, setting the WebPartStorage attribute to Storage.Personal specifies that different values of the property can be stored for different users. The effects of this code in the user interface for the task pane are shown in Figure 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;
   }
}

To ensure that the property is displayed in the task pane, all you must do is define it, as in the preceding sample code. However, you can override the GetToolParts method to customize how to display your properties. This method returns an array of ToolPart objects to be displayed in the task pane.

The following code first adds a CustomPropertyToolPart object to the ToolPart array that the method returns. Even without this code, the custom View category that contains the custom property is displayed. However, by default the custom property is displayed after the standard properties. To move the custom property to the top of the task pane, move it to the first position in the array. The CustomPropertyToolPart object automatically displays any custom properties, and the WebPartToolPart object automatically displays the standard properties. The code also specifies that the custom View category should be expanded. The other categories are collapsed by default when the task pane opens.

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

In these sample Web Parts, standard controls are automatically created to display the custom properties—a check box for the Boolean DetailViewEnabled property in the customer Web Part and a text box for the CustomerID string property in the Orders Web Part. However, you can use the Web Part infrastructure to create custom user interfaces for your properties by creating classes that inherit from the Microsoft.SharePoint.WebPartPages.ToolPart class. The Web Part templates for Visual Studio .NET include a template for creating a custom ToolPart class.

Creating Connectable Web Parts

A very impressive feature of the Web Part infrastructure is the way it supports Web Part to Web Part communication. Other control containers, beginning perhaps with the original Visual Basic "Ruby" forms package, have used an event-driven model to support interaction among controls. However, in all these forms packages (including the page framework in ASP.NET), controls raise events that are handled by code running in the container. For example, a Web form or a Windows form might include a handler for the click event of a button, and that event handler could set a property in another control on the form, perhaps triggering another event. However, there is no straightforward way for one control to handle events fired by another control.

The Web Part to Web Part communication model is different. Events are handled not by the page or even by the Web Part zone that contains the Web Part, but by handlers in other Web Parts on the page. A further challenge is created by the fact that Web Parts do not have hard-coded knowledge about which other Web Parts they are communicating with. Connections between Web Parts are created on-the-fly by users during run time.

These challenges are met by a Web Part connections infrastructure that mediates communication between Web Parts by calling standard methods of the WebPart base class for each connected Web Part on a page. To participate in Web Part to Web Part communication, your Web Part must override these methods.

Additionally, a connectable Web Part must implement one of the standard connection interfaces described earlier in this article. Web Parts can only communicate with other Web Parts that implement interfaces that are complementary to their own, although the infrastructure can provide transformers that act as adapters between certain interfaces. For example, the sample uses a transformer to connect an IRowProvider Web Part to an ICellConsumer Web Part. The transformer acts as an IRowConsumer when communicating with the IRowProvider and as an ICellProvider when communicating with the ICellConsumer.

The Web Part infrastructure calls methods of the Web Parts to discover the connectable Web Parts on each page. To make connection options available to the user, it populates menus that are added to each connectable Web Part.

Most of the connection interfaces require you to declare event delegates. The Web Part infrastructure hooks these events to handlers in other Web Parts when a user creates a connection. The infrastructure calls a series of methods for each connectable Web Part while the page is being constructed, so the Web Parts can communicate with each other by firing events that are handled by the Web Parts they are connected to.

Implementing IRowProvider

To create an IRowProvider Web Part, you must declare two events and override six methods. The sections below explain these tasks and present the sample code from the CustomerRowProvider Web Part.

Declare IRowProvider Events

The CustomerRowProvider code declares two events that are required to implement the IRowProvider interface—the RowProviderInit and RowReady events. The following code shows declarations for these events:

public event RowProviderInitEventHandler RowProviderInit;
public event RowReadyEventHandler RowReady

Technically, these two event declarations are all that this Web Part class needs to implement the IRowProvider interface. The interface does not require any event handlers because its complementary interface, IRowConsumer, does not raise any events.

However, to participate in Web Part to Web Part communication, an IRowProvider Web Part must also override six methods of the WebPart base class, even though those methods are not included in the IRowProvider interface definition. In this respect, the Web Part connection interfaces are really design patterns that require more than just implementation of the defined interfaces.

Override EnsureInterfaces

The Web Part infrastructure calls the EnsureInterfaces method for a Web Part and expects the Web Part to call the protected RegisterInterface method of the WebPart base class for each of its connection interfaces.

The RegisterInterface method takes eight parameters that give the Web Part infrastructure the information it needs to mediate connections with this Web Part. The purpose of each of the parameters is described in the comments in the following code:

 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
}

Override CanRunAt

The Web Part infrastructure uses the CanRunAt method to determine if a Web Part expects its connection to be implemented on the client or on the server. This may seem redundant because the RunAtOptions parameter of the RegisterInterface method addresses the same issue. However, the Web Part can use this method to provide logic for calculating an expected client or server setting based on data available during run time.

The Web Part infrastructure always makes the final decision about whether a connection is implemented on the client or on the server, based in part on the settings of the other Web Parts that participate in the connection.

This article and the accompanying sample do not demonstrate the creation of client-side connections, which require your Web Part to emit script code to implement the connections.

public override ConnectionRunAt CanRunAt()
{
   return ConnectionRunAt.Server;
}

Override PartCommunicationConnect

The Web Part infrastructure calls this method after a Web Part successfully connects to another Web Part. It passes parameters to the Web Part, specifying which of its interfaces was connected, which Web Part and interface are on the other side of the connection, and whether the connection is running on the client or server. In the sample, this method only calls the EnsureChildControls method. One possible use for this method is to provide code that runs only when the Web Part is connected to a specific Web Part. If you create a set of Web Parts that take advantage of special knowledge of each other, then here is where your code discovers that the Web Parts have connected.

public override void PartCommunicationConnect(
   string interfaceName,
   WebPart connectedPart,
   string connectedInterfaceName,
   ConnectionRunAt runAt)
{
   EnsureChildControls(); 
}

Override PartCommunicationInit

You must declare a RowProviderInit event, but technically you don't have to raise the event to meet the requirements of the IRowProvider interface. However, some consumer Web Parts do not work properly if you connect to them without raising the RowProviderInit event, so consider it required. The place to raise the event is when you override the PartCommunicationInit method. This event allows your code to send initialization data to the Web Part to which your Web Part is connecting.

The sample CustomerRowProvider Web Part shows how to use the RowProviderInitArgs parameter of the RowProviderInit event to publish the names of its fields and the names to be displayed for each field. A consumer Web Part that handles the event can customize itself in response, perhaps by displaying labels for the data.

Several of the Web Part communication interfaces include an Init event (CellProviderInit, CellConsumerInit and so on) with an InitEventArgs parameter that is particular to that interface. The RowProviderInitEventArgs object has FieldList and FieldDisplayList properties, which hold string arrays.

The CustomerRowProvider class in the sample has two private string array fields, _fieldList and _fieldDisplayList. These arrays are populated in the CreateChildControls method and are used here to set the FieldList and FieldDisplayList properties of the RowProviderInitEventArgs object. The code then raises the RowProviderInit event, sending this data to the event handlers of any connected Web Parts.

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

Override GetInitEventArgs

You only need to override this method for interfaces that can use transformers. The Web Part infrastructure calls this method when it creates a transformer dialog, such as the one shown in Figure 4. The method returns data that is used to customize the user interface for the dialog box.

The method has a parameter that identifies the interface being queried, and it returns an object derived from InitEventArgs. The exact type of the object that the method returns varies depending on the interface. The code in the CustomerRowProvider Web Part determines if its RowProvider interface is being queried, and if so, it returns a RowProviderEventArgs object similar to the object used when raising the RowProviderInit event.

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

Override PartCommunicationMain

Override the PartCommunicationMain method to raise any remaining events. This is usually necessary in provider Web Parts that raise events to send data to connected consumers.

IRowProvider Web Parts use this method to raise the RowReady event. The RowReadyEventArgs object has a Rows property that holds an array of ADO.NET DataRow objects.

The CustomerRowProvider Web Part populates the Rows array with a single DataRow object that matches the customer ID selected in its drop-down list. The class has a protected CurrentRow field that is populated in the drop-down list's SelectedIndexChanged event handler.

In addition to the Rows property, RowReadyEventArgs has a string property that indicates the status of the current selection. The available strings for this property are described in the comments embedded in the following code.

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

The provider interfaces support various events to be raised in PartCommunicationMain. For example, ICellProvider Web Parts raise the CellReady event, and IListProvider Web Parts raise the ListReady event. Your code must raise these events even if the current request is a postback request and the data you are providing has not changed. Some interfaces provide special events to cover situations where no data is available to be provided—for example, NoFilter, NoParametersIn and NoParametersOut. Failure to raise these events can cause problems by leaving consumer Web Parts in a state where they are waiting to handle the event.

Create Event Handlers

The final step in implementing a connection interface is to create any required handlers for the events of the complementary interface. However, the IRowConsumer interface does not include any events, so no handlers are required when you implement IRowProvider.

Implementing ICellConsumer

The OrdersCellConsumer Web Part in the sample provides an example of how to implement the ICellConsumer interface.

The steps for implementing ICellConsumer are very similar to the previous steps for implementing IRowProvider: declare events, override methods, and handle the events of your complementary interface, which in this case is ICellProvider.

The ICellConsumer interface requires only one event to be declared—CellConsumerInit, which must be raised by the PartCommunicationInit method.

Additionally, an ICellConsumer Web Part must:

  • Override EnsureInterfaces to call RegisterInterface

  • Override CanRunAt to specify a client-side or server-side connection

  • Override PartCommunicationConnect to add code that responds to being connected

  • Override PartCommunicationInit to raise the CellConsumerInit event, which sends the provider Web Part a CellConsumerInitEventArgs object that has properties for the field name and the display name of the cell being consumed

  • Override GetInitEventArgs to support transformers

  • Optionally, handle the CellProviderInit event of ICellProvider to get the FieldName and FieldDisplayName from its connection partner

  • Handle the CellReady event of ICellProvider to get data from the Cell property of a CellReadyEventArgs object

    Note   ICellConsumer Web Parts do not need to override PartCommunicationMain, because they do not raise any additional events.

Installation Details

Behind the scenes, Web Parts are supported by a rich administrative infrastructure. The Stsadm.exe tool described earlier in this article performs the following actions for each Web Part assembly that it installs:

  • Copies the Web Part assembly into the Bin directory for the SharePoint site. In the sample, this assembly is named Northwind.dll.
  • Copies the .dwp file for each Web Part in the assembly to the Wpcatalog directory for the site. A .dwp file, explained in more detail later in this article, is an XML file that contains information about a Web Part. The sample contains two .dwp files: Northwind_CustomerRowProvider.dwp and Northwind_OrdersCellConsumer.dwp. All Web Parts that have .dwp files in the Wpcatalog directory are automatically included in the Virtual Server gallery, which is a catalog of Web Parts that are available for use on that server.
  • Copies any Web Part resource files into a subdirectory that it creates for the assembly in the Wpresources directory for the site. The sample contains two resource files, Customers.xml and Orders.xml, which hold the data used by the Web Parts.
  • Adds an entry to the SafeControls section of the Web.config file for the site. The site allows users to load only those Web Parts that are listed as safe in a configuration file.
  • Copies the .cab file into the configuration database of the server running Windows SharePoint Services so you can install the assembly to other SharePoint sites or to additional servers in a server farm by referring to the assembly by name, without needing access to the original .cab file.

To permit the Stsadm.exe utility to perform these tasks, the .cab file must contain the assembly, a .dwp file, and any required resource files. Additionally, the .cab file must contain an XML file named Manifest.xml, which specifies the names of the .dwp and resource files for each assembly and the SafeControls entry to be added to the Web.config file. Here is the full text of the Manifest.xml file included with the sample:

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

You can run additional Stsadm.exe commands and command-line switches to delete Web Part packages from the server, to enumerate the installed packages, to specify that packages should be installed for specific virtual servers, or to specify that assemblies should be installed to the Global Assembly Cache (GAC) instead of a Bin folder.

Important   To install a Web Part assembly to the GAC, you must give the assembly a strong name. The Northwind sample assembly does not have a strong name, so it cannot be installed to the GAC. To give the assembly a strong name, edit the AssemblyKeyFile attribute in the AssemblyInfo.cs file to contain the path to a file that contains a key pair, and then recompile the project. For information about how to create a key pair file, see Creating a Key Pair. Using a key pair file to sign an assembly during compilation ensures that only developers with access to the private key in the key pair file can make changes to the assembly.

For a detailed explanation about how to create Web Part .cab files and how to use the options available for the Stsadm.exe tool, see Packaging and Deploying Web Parts for Microsoft Windows SharePoint Services.

Creating and Deploying .dwp Files

Windows SharePoint Services uses XML documents with the .dwp extension as file-based persistence for Web Parts. These files capture a snapshot of the property settings for a Web Part and a reference to the assembly and class used to create it.

In previous versions of Web Part technology, the .dwp file contained the actual code used to implement the logic of a Web Part. In the current version, Web Part logic is implemented in compiled ASP.NET classes in managed assemblies, and the .dwp document contains the name of the assembly and class for each Web Part.

By default, the .dwp files created by Visual Studio .NET templates specify values for only the Title property and the Description property, which are used to identify Web Parts when users add them to pages. However, you can add elements to the .dwp file to specify additional property values, overriding the default property settings implemented in the Web Part code.

The following sample shows the XML text for the Northwind_OrdersCellConsumer.dwp file that is included in the sample Northwind project:

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

The contents of this sample .dwp file are easy to understand and edit because of the self-documenting nature of XML. Note that the class name of the Web Part is specified in the TypeName element, using a name that includes the namespace.

To specify any additional property values, add a tag based on the property name and text that contains the desired property value. For example, you could add the following line to the sample .dwp file:

<CustomerId xmlns="Northwind">ALFKI</CustomerId>

By default, a Web Part installed using this version of the .dwp file would display orders for customer ALFKI.

After the shared and personal properties for a Web Part are modified by using the task pane, you can capture the current state of the Web Part as a .dwp file by selecting Export from the Web Part menu. Importing that .dwp file creates a Web Part with the same state as the one that was exported. You can use .dwp files to serialize and deserialize Web Part instances. This is especially useful when working with Web Parts such as the built-in Content Editor Web Part, which is a container for static HTML.

You can add a Web Part to a page, even if the Web Part is not yet included in a gallery, if you can locate a .dwp file for the Web Part and if the assembly for the Web Part is available in the Global Assembly Cache or in the Bin directory for the virtual server. In addition, the Web Part must be identified as safe in the virtual server’s Web.config file, as explained in the next section of this article.

To add a Web Part by referencing a .dwp file

  1. On the Web Part Page, click Modify My Page or Modify Shared Page.

  2. Point to Add Web Parts and then click Import.

  3. In the task pane, type the path to the .dwp file, or click the Browse button, and then browse to the location of the .dwp file. The selected Web Part appears in the task pane.

  4. Drag the Web Part to a Web Part zone on the page, or use the Add to menu to select a Web Part zone, and then click Add.

    CAUTION   When you add a Web Part to a site gallery, the .dwp file is copied into the content database that Windows SharePoint Services maintains for that site. If you make a change to the .dwp file, you must import it again or the change is ignored.

You can also have Windows SharePoint Services automatically create a default .dwp file for a Web Part assembly when you install the Web Part. To do this, follow these steps:

  1. On the home page, click Site Settings.

  2. On the Site Settings page, in the Administration area, click Go to Site Administration.

  3. On the Top-level Site Administration page, in the Site Collection Catalogs area, click Manage Web Part gallery.

    Note   Microsoft Windows SharePoint Services 2.0 Beta 2 used the term catalog. This term has been updated to gallery throughout this article to reflect current use.

  4. On the Web Part Gallery page, click New Web Part.

  5. On the Web Part Gallery: New Web Part page, select the check box for any Web Part that you want to add to the site gallery. These are all the Web Parts that are identified as safe in the SafeControls section of an applicable Web.config file.

  6. Optionally, type a name for the .dwp file for each Web Part that you selected. A default file name is entered automatically, based on the class name.

  7. Click Populate Gallery. A minimal .dwp file is created automatically and added to a gallery named Site_Name Gallery.

Web Parts that are added to a Site_Name Gallery become available to that site and to all sites under it.

Specifying Safe Web Parts

Allowing users or even members of the Administrator site group to have unrestricted freedom in importing new Web Parts can expose a server to security threats. For this reason, Web Parts must be explicitly designated as safe before they become available on the virtual server. This is done by making entries in the SafeControls section of a Web.config file for the virtual server. The Web.config file is typically found at this location:

system_drive:\Inetpub\wwwroot\Web.config

Each SafeControls entry identifies an assembly that contains one or more Web Parts. You can list Web Part classes individually or you can specify that all Web Parts in the assembly are safe. Here is an example of the SafeControls section from a Web.config file, with tags preceding it that show how it fits into the XML hierarchy of the file:

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

Each SafeControl element has four attributes:

  • Assembly   The name of an assembly that contains one or more Web Parts. For assemblies that are strong named, you must include the name, version, culture and public key token. For other assemblies, only the name is required, although all four parts may be included.

    CAUTION   If you give a strong name to the sample Northwind assembly by adding the path to a key pair file in the Assemblyinfo.cs file for the project, you must add the public key token to the SafeControl entry for that assembly. An easy way to get a strong-named assembly's public key token is to add the assembly to the GAC by dragging the .dll file into the local_drive:\Windows\Assembly directory. You can then right-click the assembly and click Properties to see the properties of the assembly. The public key token is displayed, and this view of the token is handy because you can select it and copy it to the clipboard. If you want to remove the assembly from the GAC, right-click the assembly, and then click Delete.

  • Namespace   The .NET namespace for the Web Part class. Note that Web Parts in nested namespaces must be listed separately, even if an asterisk is entered for TypeName. For example, the SafeControls XML section shown earlier includes two separate entries for the assembly Microsoft.SharePoint—one for the Microsoft.SharePoint namespace and one for the Microsoft.SharePoint.WebPartPages namespace.

  • TypeName   The class name of the Web Part. You can use an asterisk (*) to indicate that an entry applies to all Web Part classes in the specified assembly and namespace. This is especially handy when you are developing the assembly, because no changes are required as you add new Web Part classes and recompile your assembly.

  • Safe   This attribute usually has a value of True. However, members of the Administrator site group can deny the safety of a Web Part and make it unavailable by setting this attribute to False.

The SafeControls listing prevents rogue Web Parts from becoming available to users. Additionally, Web Parts are subject to the same Code Access Security controls that are applied to all managed code.

Code Access Security

Among the valuable services that the Common Language Runtime provides for Web Parts is code access security. Your Web Part code can be prevented from performing certain types of actions, such as reading or writing files, according to policies that are under the control of members of the local computer Administrator account.

In keeping with Microsoft's effort to provide trustworthy computing, the default security settings for Windows SharePoint Services are very restrictive. You will probably need to be proactive to ensure that your Web Part assemblies get the permissions they require.

The Runtime evaluates various types of evidence for each .NET assembly. For example, a strong name is a type of evidence, as is the location of an assembly—whether it is installed in the GAC or in a bin folder. The Runtime also evaluates configuration files that specify security policies. Based on the evidence and the policies, each assembly is granted a set of permissions.

Note   Microsoft Windows SharePoint Services 2.0 Beta 2 included a very permissive security policy by default. Many Web Parts that functioned perfectly in Beta 2 may generate security exceptions when run on later Beta versions or on the commercial version of Microsoft Windows SharePoint Services 2.0. For example, the sample Northwind Web Parts require file access permissions to read data from XML files—these permissions were available by default in Beta 2 but not in later versions.

The following sections describe several techniques you can use to enable functionality in Web Parts by adjusting code access security evidence and policies. For information about code access security, see the Microsoft SharePoint Products and Technologies Software Development Kit (SDK).

Specifying a Trust Level in the Web.config File

The Web.config file for a SharePoint site contains a trust element that specifies the default level of security granted to Web Parts running on that server. This element appears in the System.Web section of the configuration file:

<trust level="WSS_Minimal" originUrl="" />

The values for the level attribute that are available by default are Full, High, Medium, Low, Minimal, WSS_Medium and WSS_Minimal. Only three of these levels permit Web Parts to run: Full, WSS_Medium, or WSS_Minimal. The other trust levels apply to ASP.NET but don't include the specific permissions needed by Web Parts.

One easy—but potentially dangerous—way to enable functionality in your Web Parts is to upgrade the trust level in the Web.config file. For example, the Northwind sample Web Parts require two types of file I/O (input/output) permissions—they need to call Page.Server.MapPath to discover the file system path to the XML data files, and they need to read the files. These permissions are not available with a trust level of WSS_Minimal, but they become available if you change the value to WSS_Medium or Full.

Using this method of elevating Web Part permissions is dangerous because it is applied globally to the entire SharePoint virtual server. A better practice is to use a technique that allows you to discriminate between different Web Parts, applying different levels of trust as necessary. For debugging and development work, however, varying the setting in the Web.config file can be very useful.

To make sure that any changes you make to the Web.config trust level setting take effect, run Iisreset from a command prompt after you save the change.

Creating and Editing Policy Files

Each of the trust levels available in the Web.config file corresponds to a policy configuration file that specifies a set of permissions. For example, the permissions for the WSS_Minimal trust level are specified in a file named Wss_minimaltrust.config.

You can edit these files to change the default policies. You can also create your own policy files and refer to your custom policies in the Web.config file. The SecurityPolicy section of the Web.config file lists available custom trust levels and the files upon which they are based. The following code shows the default listing in a Web.config file for a SharePoint site:

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

You can add trust level elements that reference your own custom policies. Each policy file contains a list of permission classes, a list of named permission sets with the permissions for each set, and a list of code groups that define the evidence an assembly must provide to be assigned a particular permission set.

For information about creating a custom policy file for Web Parts, see the Microsoft SharePoint Products and Technologies Software Development Kit (SDK).

You can also edit one of the default policy files. For example, you can modify the Wss_minimaltrust.config file to grant the necessary file I/O permissions to the Northwind Web Parts even when the server trust level is set to WSS_Minimal.

Three steps are required to perform this modification:

  • Specify any required permission classes.
  • Define permission sets.
  • Define code groups that assign permission sets based on evidence.

First, add the following lines to the SecurityClasses section of the Wss_minimaltrust.config file on your server:

 <SecurityClass Name="FileIOPermission"
   Description="System.Security.Permissions.FileIOPermission, mscorlib,
   Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

Next, add the following permission set to the NamedPermissionSets section. This set includes the permissions required for Web Part execution and Web Part connections, plus a file I/O permission that is limited to reading and discovering the paths of files in the Wwwroot directory tree for the virtual server.

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

The final modification you must make to the policy file is to add a code group that assigns your custom permission set to assemblies that meet the membership criteria you specify—the evidence. In this example, membership in the code group depends on having a specified strong name. This is a safe way of limiting the elevated permissions to a single assembly.

Code group blocks of XML are nested in categories. Each code group definition contains two elements, a CodeGroup element and an IMembershipCondition element.

Locate the outermost CodeGroup element that has the value of FirstMatchCodeGroup for its class attribute. Its PermissionSetName attribute is set to Nothing because it is a container for other code groups that reference named permission sets. Nest the following lines inside that outer FirstMatchCodeGroup code group:

<CodeGroup 
   class="UnionCodeGroup" version="1"
   PermissionSetName="NorthwindPermissionSet">
      <IMembershipCondition 
      class="StrongNameMembershipCondition"
      version="1" PublicKeyBlob="your public key"/>
</CodeGroup>

The sample Northwind project contains a file named Wss_mimimaltrust.config that includes these changes.

One last step is required to make this work. You need to substitute an actual public key value for the "your_public_key" token in the IMembershipCondition element. To do this, use the Sn.exe tool to generate a keypair file, reference the keypair file in the AssemblyKeyFile attribute of your Assemblyinfo.cs file as explained earlier, and recompile the Northwind project to generate a strong-named assembly. To get the strong name blob, run the following command from a command prompt:

  secutil.exe –hex –s Northwind.dll

Also, remember that if you give a strong name to your Web Part assembly, you should also add the public key token value to the SafeControl entry for that Web Part in the Web.config file, as explained earlier.

Installing to the GAC for Full Trust

If you give your Web Part assembly a strong name, you have one more option for elevating its trust level. You can install your assembly in the Global Assembly Cache so it automatically executes with full trust.

The reason for this becomes clear if you inspect the policy files for Windows SharePoint Services. Even the Wss_minimaltrust.config file includes the following code group:

<CodeGroup
   class="UnionCodeGroup" version="1"
   PermissionSetName="FullTrust">
      <IMembershipCondition class="UrlMembershipCondition"
        Url="$Gac$/*" version="1"/>
</CodeGroup>

When you use the Stsadm.exe utility to install a Web Part .cab file for an assembly that has a strong name, you can use the globalinstall command line switch to install it to the GAC, as follows:

Stsadm.exe –o addwppack –filename path_to_Web_Part.cab file -globalinstall

You can also manually install a strong-named Web Part assembly to the GAC by dragging the .dll file for the Web Part to the following special folder:

local_drive:\Windows\Assembly

A special Wpresources folder location is used for all Web Parts that are installed to the GAC:

local_drive:\Program Files\Common Files\Microsoft Shared\
Web Server Extensions\wpresources

One advantage of using URLs based on the Wpresources directory is that the Web Part infrastructure automatically looks for your files in the appropriate location, depending on whether a Web Part is installed to the bin directory for a virtual server or to the GAC.

However, relying on the GAC is another example of an easy but potentially dangerous way to add permissions to your Web Part, because Web Parts installed in the GAC always run with full trust. A better practice is to give your code only the permissions that it needs, and no more. Web Parts installed to the GAC also automatically become available to every virtual server on that computer, which you may not want.

When you install to a Bin directory, you can limit your Web Part to a single virtual server and limit the permissions available to your Web Part.

Note   One further step you can take to facilitate restricting permissions is to add attributes to your Web Part assemblies that specify exactly which permissions they require. This adds metadata to your assembly that makes it easier to discovery which permissions are needed. The following is an example of a security attribute that prevents the assembly from loading if file I/O permissions are not available:

using System.Security.Permissions;

[Assembly: FileIOPermission(SecurityAction.RequestMinimum)]

Debugging Web Parts

Visual Studio .NET makes it easy to step through the code in the class for a Web Part during page execution. However, to debug a Web Part, you must first make sure that it is installed on your server running Windows SharePoint Services or you won't be able to run it. The actions itemized in the following steps are explained in more detail earlier in this article:

  1. Set the Output Path property for your Web Part project to point to the Bin directory for your virtual server.
  2. Install your Web Part from a .cab file, or follow the next three steps instead.
  3. Add an entry to the SafeControls section of the Web.config file for your virtual server.
  4. Create a .dwp file for your Web Part.
  5. Load your Web Part to your Site_Name Gallery. To do this, follow these steps:
    1. On the Web Part Page, click Modify My Page or Modify Shared Page.
    2. Point to Add Web Parts, and then click Import.
    3. In the task pane, type the path to the .dwp file, or click the Browse button and browse to the location of the .dwp file. The selected Web Part then appears in the task pane.
    4. Drag the Web Part to a Web Part zone on the page, or select a Web Part zone from the Add to list, and then click Add.

You may want to add the Web Part to a page before you begin debugging, or you may want to debug the code that runs when a Web Part is added. Either is possible.

When you are ready to start debugging, return to Visual Studio .NET and set one or more break points in the code for your Web Part. Two good candidates for methods to break into are the CreateChildControls method and the RenderWebPart method.

The next steps may be the trickiest, if you have never debugged an ASP.NET custom control:

  1. On the Debug menu, click Processes.

  2. In the Processes dialog box, make sure that the check boxes for Show system processes and Show processes in all sessions are both selected.

  3. Select the W3wp.exe process.

  4. Click Attach.

  5. In the Attach to Process dialog box, select Common Language Runtime as the program type you want to debug, and then click OK.

  6. Click Close.

  7. Run your Web Part in the browser; execution breaks at your break points.

  8. Use standard Visual Studio techniques to debug your code, and select Stop Debugging from the Debug menu when you are ready to end the session.

    CAUTION   If you attempt to edit and recompile your code after you test your Web Part, you may receive an error message that indicates that Visual Studio .NET was unable to replace the output .dll file. To solve this, use Task Manager to end the W3wp.exe process or run

    iisreset

    from a command line. This releases the lock on the .dll file so you can recompile.

For more information about debugging Web Parts, see Debugging Web Parts.

Summary

For companies using Windows SharePoint Services, which is currently available only with Microsoft Windows Server 2003, Web Parts present very compelling technology. The combination of the collaboration environment of Windows SharePoint Services and the ASP.NET extensibility model finally provides developers with a platform for realizing the promise behind the original Digital Dashboard vision.

For users, Web Parts provide a new level of freedom and power to assemble personalized pages, workspaces, and portals. For administrators, Web Parts provide safety and scalability. For architects, the Web Part infrastructure brings the benefits of interface-based, pluggable components to the presentation tier of Web applications.

About the Author

Andy Baron is a senior consultant and project manager at MCW Technologies, a Microsoft Certified Partner. Andy also creates training materials for AppDev, and he has received Microsoft's MVP designation every year since 1995.