Sponsored Links


Resources

.NET Research Library
Get .NET related white papers, case studies and webcasts

Making SharePoint Web Parts InteractMaking SharePoint Web Parts InteractMaking SharePoint Web Parts Interact Discuss DiscussDiscuss Printer friendly Printer friendlyPrinter friendly
Making SharePoint Web Parts Interact

June 15, 2005

Most Web sites, and portals in particular, make a point of showing large amounts of content. This can be a feast for users, but in the long run it can also be a source of confusion. Recent studies on Web usability have concluded that personalization is a key factor in successful Web sites. Giving users the tools to build a personalized view of the contents can mean the difference between an average and a superior Web site. You could say that cutting-edge Web sites are rich in content, have a consistent and modular design, and allow users to personalize the content. SharePoint-based Web sites have for sure a modular design and allow personalization.

Contents of SharePoint sites don’t necessarily depend on the underlying infrastructure, but an extremely flexible and modular plumbing definitely helps page developers to deliver rich contents whose layout and structure is decided by the end user. SharePoint Web Parts are the building blocks of many pages as they represent a window of data opened in the user’s display. Imagine a navigation system that allows a power user (i.e., a manager) to drill down into a mine of data blocks—employees, customers, orders—to cross data and extrapolate numbers to make important business decisions. Would you really build all of this logic into a single Web Part? No, I’m sure you wouldn’t.

First off, the complexity of development will soon grow beyond any reasonable threshold. Second, unit testing gets hard. (You don’t want to rush off to the manager’s office whenever something goes wrong, do you?) Third, a monolithic implementation of such a complex functionality does somewhat violate the spirit of Web Parts. Web Parts are relatively simple and composable components that you use to build complex and personalizable pages. The idea, therefore, is building simpler Web Parts that can connect to each other, share data, and complement each other’s behavior. Put together, they can form an extensible, highly personalizable mechanism to have developers and power users build rich SharePoint contents.

This article explains the mechanics of connectable Web Prts and demonstrates how to build two Web Parts the can work together according to a master/detail schema.

The Connection Model

Two connected Web Parts operate in a publisher/subscriber fashion. Any change in the values exposed by the provider are immediately reflected by the consumer. As you can imagine, this model lends itself very well to representing master/detail models of data that are also extensible at will. The Web Part connection model consists of two interoperating entities—a provider and one or more consumers.

The provider is called to collect its public data and make it available to registered callers. Consumers are called to retrieve exposed data and update their own user interface accordingly. The figure below provides a high-level view of the connection model.

FIGURE 1—A high level view of the Web Parts connection model

Note that this article covers only the ICellProvider/ICellConsumer connection model. In general, there are quite a few pairs of interfaces that Web Parts can implement to be connectable. I’ll return on this point at the end of the article. For now, let’s focus on the simplest pair of connecting interfaces, that is ICellProvider and ICellConsumer.

A provider Web Part is a Web Part that implements the ICellProvider interface; a consumer Web Part, on the other hand, is required to implement ICellConsumer. The ICellProvider interface defines the following members.

public interface ICellProvider
{
  // Events
  event CellProviderInitEventHandler CellProviderInit;
  event CellReadyEventHandler CellReady;

  // Methods
  void CellConsumerInit(object sender,
       CellConsumerInitEventArgs cellConsumerInitEventArgs);
}

A provider must contain the code to fire a couple of events to consumers and contain the code that handles a consumer’s event. The ICellConsumer interface is symmetrical.

public interface ICellConsumer
{
  // Events
  event CellConsumerInitEventHandler CellConsumerInit;

  // Methods
  void CellProviderInit(object sender,
       CellProviderInitEventArgs cellProviderInitArgs);
  void CellReady(object sender,
       CellReadyEventArgs cellReadyArgs);
}

The two interfaces are specular: each event fired by one interface finds a handler method in the other. As an example, let’s consider the CellReady event on ICellProvider. By design, the event is fired to let registered consumers know that new data is available. How can a consumer be notified of that? It implements the CellReady method on ICellConsumer. Note that there’s no direct interaction between the two Web Parts. An intermediate component—the SharePoint authoring environment—takes care of mapping events to methods in both directions.

Rendering Connected Web Parts

In addition to the aforementioned interfaces, there are a few more methods involved in the Web Parts interaction that are defined on the base WebPart class. The SharePoint authoring environment invokes them in the pre-rendering phase of the Web Part. The complete sequence of steps is outlined below and gives a clear idea of the lifecycle of connected Web Parts.

  • For each pair of connected Web Parts, the Web Part infrastructure calls EnsureInterfaces on the provider and consumer. EnsureInterfaces is a WebPart overridable method that provides the infrastructure with information on the interface type being implemented, a reference to the Web Part, and the maximum number of allowed connections.
  • For each pair of connected Web Parts, the Web Part infrastructure calls the CanRunAt method to learn from where the Web Part can run—client and/or server based on the current configuration.
  • Method PartCommunicationConnect is called on the provider and next on the consumer. Both Web Parts are notified that the connection has been established and ensure their child controls are created.
  • Method PartCommunicationInit is called on the provider. The method can optionally fire the CellProviderInit event. If fired, the event is handled by the infrastructure and resolved invoking the corresponding method on the consumer. In the CellProviderInit event, the provider supplies a description of the field(s) it is publishing. In the corresponding method, the consumer can check what type of field the Provider will be sending. Should the field be unacceptable, an exception would be thrown.
  • Method PartCommunicationInit is also called on the consumer and can optionally fire the CellConsumerInit event. If fired, the event will be handled by the infrastructure and served through the provider’s CellConsumerInit method.
  • Method PartCommunicationMain is invoked on the provider to pack any public data and notify the consumer through the CellReady event. Again, the event is captured by the infrastructure and handled through the consumer’s CellReady method.

The following code snippet shows a typical implementation of the PartCommunicationMain method on a provider Web Part. The example I’m considering is based on a Web Part named EmployeeViewer which exposes an integer property—EmployeeID. Through the following code, the Web Part publishes the ID of the currently selected employee.

public override void PartCommunicationMain()
{
   if (CellReady != null)
   {
     CellReadyEventArgs cellReadyArgs = new CellReadyEventArgs();
     cellReadyArgs.Cell = _employeeID;
     CellReady(this, cellReadyArgs);
   }
}

The code below contains the typical consumer's answer to the CellReady event.

public void CellReady(object sender, CellReadyEventArgs cellReadyArgs)
{
   if(cellReadyArgs.Cell != null)
   {
      _employeeID = (int) cellReadyArgs.Cell;
   }
}

The consumer stores the passed data into a private member for further use. Once all the steps above have been accomplished, the OnPreRender event is fired on all Web Parts and their contained controls.

It is important to note that of all the methods and events listed above, only the provider’s CellReady event and consumer’s CellReady method are really essential.

Implementing the Provider

If you make use of the SharePoint 2003 template for Visual Studio .NET 2003, creating provider and consumer Web Parts couldn’t be easier. You start by creating a Web Part project and then add a new item to the project. As the figure demonstrates, you have only to pick up the right item from the list.

FIGURE 2—Select and add a provider or consumer Web Part

The wizard automatically creates a new Web Part class for you that already implements the ICellProvider or ICellConsumer interface.

My goal is creating a EmployeeViewer Web Part that shows some personal information about a given employee. The ID of the employee is a public attribute of the Web Part and is defined via the property below.

[Browsable(true),
	Category("Miscellaneous"),
	Description("The ID of the selected employee"),
	DefaultValue(1),
	FriendlyName("EmployeeID"),
	WebPartStorageAttribute(Storage.Personal)]
public int EmployeeID
{
	get {return _employeeID;}
	set {_employeeID = value;}
}

Needless to say, the _employeeID is a private integer member of the Web Part class. As mentioned above, in the CellReady event you pack the value of this member and pass it to the consumer. Being browsable and personalizable, the property also shows up in the standard editing box of the Web Part. (See the figure below.)

FIGURE 3—Editing the EmployeeID property

The rendering code of the Web Part runs a query against SQL Server, grabs personal information about the specific employee ID and drafts an HTML table to show it nicely. The database of choice is Northwind.

Implementing the Consumer

Writing the consumer Web Part is no harder than arranging a provider. You repeat the same steps as above to add the skeleton of a consumer Web Part to your project. If both parts are contained in the same assembly, you need to create a second DWP file. In the simplest case, you only need to override the CellReady method, retrieve any values the provider is passing and cache that internally.

The CellReady method receives an object of type CellReadyEventArgs in which the Cell property points to any value the provider has packed in the same object in its PartCommunicationMain method. The Cell property is of type object and must be cast to the right type before use.

The consumer might want to check if the provider is passing the right data. To obtain that, you give a non-empty implementation to the CellProviderInit method. Note that this method can’t be omitted as it is part of the ICellConsumer interface.

public void CellProviderInit(object sender,
       CellProviderInitEventArgs cellProviderInitArgs)
{
  if (cellProviderInitArgs.FieldName != "EmployeeID")
  {
     throw new NotSupportedException();
  }
}

The CellProviderInitEventArgs class is originally filled by the provider when it fires the CellProviderInit event. Here’s some standard code that does this.

public override void PartCommunicationInit()
{
   if (CellProviderInit != null)
   {
     CellProviderInitEventArgs cellProviderInitArgs;
     cellProviderInitArgs = new CellProviderInitEventArgs();
     cellProviderInitArgs.FieldName = "EmployeeID";
     cellProviderInitArgs.FieldDisplayName = "Employee ID";
     CellProviderInit(this, cellProviderInitArgs);
   }
}

The FieldName property indicates the name of the data object being passed. The value you assign has no syntactical relevance—it can be any string that provider and consumer agree on as the name of the field being exchanged.

The sample consumer Web Part reads the employee ID from the connected provider and shows all the orders he or she managed in the specified year. The year to view is a public and browsable property defined as follows.

[Browsable(true),
   Category("Miscellaneous"),
   Description("Year of selected orders"),
   DefaultValue(1997),
   FriendlyName("Year"),
   WebPartStorageAttribute(Storage.Personal)]
public int Year
{
   get {return _year;}
   set {_year = value;}
}

Employee ID and year are both parameters used to populate a SQL query whose results are displayed through a dynamically created DataGrid.

private void BuildUI(HtmlTextWriter writer)
{
   DataGrid _grid = new DataGrid();
   _grid.AutoGenerateColumns = true;
   _grid.Font.Name = "verdana";
   _grid.Font.Size = FontUnit.Point(8);
   _grid.HeaderStyle.Font.Bold = true;
   _grid.HeaderStyle.BackColor = Color.PaleTurquoise;
   _grid.HeaderStyle.HorizontalAlign = HorizontalAlign.Center;

   _grid.DataSource = _data;
   _grid.DataBind();
   _grid.RenderControl(writer);
}

The figure below shows the two Web Parts working together in the same Web page.

FIGURE 4—Two connected Web Parts in action

As shown in the figure, the title of the OrdersViewer Web Part is dynamically set based on the selected year. From the figure, you also see that multiple instances of the OrdersViewer consumer Web Part can be concurrently bound to the same provider. (This is possible as long as the provider doesn’t pose a limit on the supported connections.)

The idea of the example is, using different order viewers for different years. For a better graphical result, each consumer Web Part must refresh its title to reflect the value of the Year property. The title of a Web Part can only be set before the pre-rendering phase begins. Good places are the OnLoad overridable method of ASP.NET server controls or, in this particular case, the set accessor of the Year property.

Setting Up the Connection

So far I discussed how to write a provider and a consumer, which interfaces make them work together, and even the details of the internal infrastructure. No words have been said, though, about how two Web Parts are physically connected. An explicit link between two Web Parts—provider and consumer—must be created and is needed to let the SharePoint infrastructure go through the steps described in the beginning of the article.

To create a connection manually, you first put the page in design mode and then click on the Web Part menu of the provider (or the consumer). You select the Connections popup menu and follow the instructions. The figure below shows the results for the sample page.

FIGURE 5—Connecting Web Parts

Other Connection Models

The connection between two Web Parts is based on a pair of mirror-like interfaces and an intermediate authoring system that calls methods on both sides as appropriate. Connection interfaces differ for the amount of data they move, as in the table below.

Interfaces

Description

ICellProvider, ICellConsumer

Let Web Parts exchange a single value

IRowProvider, IRowConsumer

Let Web Parts exchange a single row of information

IListProvider, IListConsumer

Let Web Parts exchange a list of data

IFilterProvider, IFilterConsumer

Let Web Parts exchange a filter expression that contains one or more pairs of column names and values

TABLE 1—Common provider/consumer pairs of interface

In general, a Web Part that implements a consumer interface can connect to, and receive data from, a Web Part that implements a compatible provider interface. Compatible and complementary interfaces are listed in the table.

All the interfaces in Table 1 can be connected each other in a browser. There are other two interface pairs you can use to connect Web Parts in FrontPage 2003, but not in a browser.

Interfaces

Description

IParametersOutProvider, IParametersOutConsumer

The provider interface defines a set of parameters it can send to the consumer

IParametersInProvider, IParametersInConsumer

The consumer interface defines a set of parameters it can receive from the provider

TABLE 2—FrontPage 2003 only interfaces

The difference between the IParametersOut and IParametersIn interface pairs is subtle. In one case, the provider defines which parameters are to be passed to the consumer. In other case, the consumer determines which parameters are to imported. Normally, the consumer plays a passive role with respect to the provider. A consumer that implements IParametersInConsumer interface reverts the rule and dictates conditions.

There might be cases in which a provider can’t be connected to the right consumer. On purpose, the Web Parts infrastructure provides several transformers that allow Web Parts to be connected to each other even when their interfaces are not coincident. Supported transformations are the following:

Transformation

How it works

IRowProvider to ICellConsumer

A dialog box appears to let you choose which cell within the row is going to be mapped

IRowProvider to IFilterConsumer

A dialog box appears to force the consumer to select a single column in the row to filter on

IParametersOutProvider to IParametersInConsumer

A dialog box appears that the user can use to define how the parameters from the provider should map to the parameters of the consumer. Not connectable in a browser.

IRowProvider to IParametersInConsumer

A dialog box appears to let users map columns in the row to the parameters of the consumer. Not connectable in a browser.

TABLE 3—Built-in interface transformers

Some Web Parts connections can span over two distinct pages. The following table shows the interfaces that can be connected across pages. Bear in mind that in order to connect Web Parts located on different pages, you must use FrontPage 2003.

In the source page

In the target page

IRowProvider

IFilterConsumer

IRowProvider

IParametersInConsumer

IFilterProvider

IFilterConsumer

IParametersOutProvider

IParametersInConsumer

IParametersInProvider

IParametersInConsumer

TABLE 4—Interfaces supported in a cross-page connection scenario

The source page must contain a provider Web Part implementing any of the interfaces in the first column. The target page must contain a Web Part that implements the corresponding interface listed in the second column.

Final Remarks

Looking back at the sample Web Part provider created in this article, it should be clear that making it implement the IRowProvider interface instead of ICellProvider would have made it much more versatile. Implementing IRowProvider makes the Web Part work with a variety of consumers—row consumers, filter consumers, parameters-in consumers and, of course, cell consumers. A cell provider is limited to connecting to cell consumers. It works, but it’s not a smart way of organizing your code.

A second remark regards a frequently asked question. Is it possible for a Web Part to implement both provider and consumer interfaces? Sure it is. In doing so, though, possible name conflicts can generate compile errors. For example, CellReady is an event on ICellProvider and a method on ICellConsumer. If both interfaces are implemented in the same source class, the conflict is inevitable unless you explicitly tell the compiler about the interface behind that member. The following code snippet shows how to go.

public class Foo : WebPart, ICellConsumer, ICellProvider
{
   public event CellReadyEventHandler CellReady;
   :
   void ICellConsumer.CellReady(object sender, CellReadyEventArgs e)
   {
      :
   }
}

Summary

Connectable Web Parts add a new level of versatility and power to SharePoint and related Web pages. Connectable Web Parts are similar to ordinary Web Parts, except that they implement special interfaces that let them communicate with other Web Parts at run time.

Involved interfaces are no rocket science and emerge atop a few basic concepts that revolve around the classic publisher/subscriber pattern.

There are several different pairs of compatible interfaces that you can use. Choosing the right pair doesn’t necessarily make your end pages look better or run faster. Choosing the right pair, though, can save you—as a developer—from rewriting boilerplate code over and over again. In one word, choosing the right pair of interfaces helps architecting better your company’s toolkit for SharePoint sites.

Authors

Dino Esposito is a trainer and consultant for Wintellect based in Rome, Italy. He runs the Cutting Edge column for MSDN Magazine and is a regular contributor to MSDN News and SQL Server Magazine. Before becoming a full-time author, a full-time consultant and a full-time trainer, Dino was a full-time employee and worked day and night for Andersen Consulting focusing on the very first real-world implementations of DNA systems. Dino also has extensive experience developing commercial Windows-based software, especially for the photography world, and was part of the team who designed and realized one of the first European image online databanks. To get in touch, just write to dinoe@wintellect.com. To meet in person, just attend leading conferences such as Microsoft TechEd, VSLive!, VS DevCon, WinSummit or Wrox WebDev Conference.

News | Blogs | Discussions | Tech talks | White Papers | Downloads | Articles | Media kit | About
All Content Copyright ©2007 TheServerSide Privacy Policy
Site Map