Partager via


Creating a Connectable Web Part

This programming task describes how to create two connectable Web Parts: a Web Part that can consume a single cell value and another Web Part that can provide a single cell value.

About connectable Web Parts

The Web Part architecture provides a standardized set of interfaces called connection interfaces that allow Web Parts to exchange information with each other at run time. For example, the List Web Part that is built into Microsoft Windows SharePoint Services can provide (send) a row of data to any other Web Part that can consume (receive) that row, such as a Web Part that implements a form to display the row.

Because the Web Part architecture provides a standard set of connection interfaces, connectable Web Parts can be developed by entirely different developers or companies to communicate with one another. A Web Part that supports connection interfaces can be connected by an end user with either Microsoft Office FrontPage or a Web browser. This allows end users to build sophisticated combinations of Web Parts through a simple menu-driven user interface.

Connection interfaces

Connection interfaces are paired events relevant to a specific item, such as a row in a list. The paired interfaces form a communication bus between Web Parts that implement them. A connectable Web Part raises an interface event to one or more connected parts to make them perform an action. Interfaces are paired as a provider to a consumer. Events from the provider get handled in the consumer and vice versa. The following table briefly describes each pair of connection interfaces.

Connection interface pair Description
ICellProvider, ICellConsumer Connection interfaces for providing or consuming a single value item, such as a cell or field.
IRowProvider, IRowConsumer Connection interfaces for providing or consuming a single row (or multiple rows) of values.
IListProvider, IListConsumer Connection interfaces for providing or consuming an entire list.
IFilterProvider, IFilterConsumer Connection interfaces for providing or consuming a filter value. For example, the SharePoint List Web Part supports IListProvider, IRowProvider, and IFilterConsumer. Because IRowProvider can connect to IFilterConsumer, two different SharePoint Lists can be connected to one another. This allows one list to filter the other connected list.
IParametersInProvider, IParametersInConsumer The IParameterIn interfaces allow passing and receiving of any set of arbitrary parameters between Web Parts. These interfaces cover a situation where the consumer Web Part owns the parameter list and needs to communicate this to other Web Parts.
IParametersOutProvider, IParametersOutConsumer The IParameterOut interfaces allow passing and receiving of any set of arbitrary parameters between Web Parts. These interfaces cover a situation where the provider Web Part owns the parameter list and needs to communicate this to other Web Parts.

Compatibility rules

Web Parts can only be connected if they are compatible. Shown below are several compatibility rules which are assessed by the Web Part architecture when determining whether two Web Parts can be connected. If the Web Parts are found to be incompatible, the Connection menu item in the browser will be dimmed and a tool tip will explain the reason for incompatibility.

Opposite pairs

A connection interface can only connect with other compatible interfaces. The most basic compatibility rule is that interfaces must be connected as opposite pairs or connected through a transformer. Connecting as opposite pairs means that a Web Part that implements the IRowProvider interface can connect with another part that implements the IRowConsumer interface.

Transformers

The Web Part architecture provides a number of transformers which allow two dissimilar interfaces to connect. For example, an IRowProvider Web Part can connect to an ICellConsumer Web Part through a transformer. The transformer defines how fields/columns from the IRowProvider Web Part map to the ICellConsumer part. The following transformers are provided by the Web Part architecture.

Transformer Connect in browser Connect in Microsoft Office FrontPage
IRowProvider to ICellConsumer Yes Yes
IRowProvider to IFilterConsumer Yes Yes
IParametersOutProvider to IParametersInConsumer No Yes
IRowProvider to IParametersInConsumer No Yes

Cross-page connections

Some interfaces are allowed to connect to Web Parts on a different page. The behavior is similar to a hyperlink. The following interfaces support cross-page connections. Note that a Web page editor that is compatible with Windows SharePoint Services such as Microsoft Office FrontPage 2003 is required to author cross-page connections. However, once a cross-page connection has been formed, it can be used by any supported Web browser at run time. Additionally, only connectable Web Parts that have implementations designed to run on the server (CanRunAt = ConnectionRunAt.Server or CanRunAt = ConnectionRunAt.ServerAndClient ) can establish cross-page connections.

Source page interface Target page interface
IRowProvider IFilterConsumer
IRowProvider IParametersInConsumer
IFilterProvider IFilterConsumer
IParametersOutProvider IParametersInConsumer
IParametersInProvider IParametersInConsumer

For more information, see Cross-page Web Part Connections.

Client and server connections

At any given time, Web Parts are allowed to run on the client or the server. Some Web Parts, if designed accordingly, can detect the conditions under which they are running and dynamically switch to run on the client or the server. Web Parts can only be connected to other Web Parts running in the same location. For example, server-side parts can only be connected to other server-side parts. Server-side parts cannot be connected to client-side parts. The connection chain must be homogenous. If a part can dynamically switch between client or server connections, the Web Part architecture will automatically pin the Web Part to be a client- or server-side part depending upon the Web Part chain to which it is being connected.

Maximum number of connections

When registering an interface, the maximum number of connections to other Web Parts can be specified. The options are 1 or unlimited. If the connection limit is exceeded on a Web Part, it cannot be connected to other parts.

No circular connections

A Web Part cannot be connected to itself, either directly or through a chain of connections.

Shared and private Web Part connections

A shared Web Part can be connected to a private Web Part, if the private Web Part is a consumer and the shared Web Part supports an unlimited number of connections (MaxConnections).

Program flow

Connected Web Parts pass information to each other by firing specific interface events. When a Web Part implements an interface such as ICellProvider, it must override a number of methods. The firing of the interface events is performed by the Web Part architecture calling into the overridden methods at designated times. The following steps for creating connectable Web Parts define which methods need to be overridden and the typical code that a Web Part author should use to do so.

Creating a Web Part that implements the ICellProvider interface

This programming task defines the process of creating a class that implements all of the necessary methods and events for a connection interface using the ICellProvider interface. For a complete code example, refer to the ICellProvider and ICellConsumer source code samples at the end of these steps.

To start this programming task  

  • Perform the steps described in Creating a Basic Web Part up to the steps for "Defining the Rendering and Logic of Your Web Part."

There are 11 high-level steps that you must complete to implement a connection interface for your Web Part:

  • Create the interface class.
  • Declare events.
  • Override the EnsureInterfaces method, and then call the RegisterInterface method.
  • Override the CanRunAt method.
  • Override the PartCommunicationConnect method.
  • Override the PartCommunicationInit method.
  • Override the PartCommunicationMain method.
  • Override the GetInitEventArgs method.
  • Implement the interface event handlers.
  • Override the RenderWebPart method.
  • Implement supporting methods.

Step 1: Create the interface class

Create a class that implements one of the predefined connection interfaces. In this example, we'll implement the ICellProvider interface. For the remaining steps (2-11), all of the code goes in this class.

Example

public class CellProvider : WebPart, ICellProvider

{
       // Lots of code goes here. See steps 2 – 11.
}

Step 2: Declare events

Declare all of the relevant events for the connection interface. In this location, other variables used by the Web Part will also be declared. Because we're working with the ICellProvider interface, the following example declares variables for its CellProviderInit and CellReady events.

Example

// CellProviderInit Event

public event CellProviderInitEventHandler CellProviderInit;

// CellReady Event

public event CellReadyEventHandler CellReady;

Step 3: Override the EnsureInterfaces method, and then call the RegisterInterface method

Override the EnsureInterfaces method, which is a member of the WebPart base class. The EnsureInterfaces method will be called by the Web Part architecture before the Web Part renders. This is the time when the Web Part should register all of its interfaces by calling one of the two RegisterInterface methods, which are members of the WebPart base class.

Must Override? Yes

Example

public override void EnsureInterfaces()
{
   // Register Interfaces      (See following section)

}

Within the overridden EnsureInterfaces method, you notify the Web Part architecture about the interfaces that will be used by calling the RegisterInterface method. As noted in Step 3, the RegisterInterface method occurs within the EnsureInterfaces method. The RegisterInterface method takes several parameters, which are defined below.

Must Override? N/A

Method definition

protected InterfaceProperties RegisterInterface(string interfaceName,
                     string interfaceType,
                     int maxConnections,
                     ConnectionRunAt runAtOptions,
                     object interfaceObject,
                     string interfaceClientReference,
                     string menuLabel,
                     string description)

Note  A second RegisterInterface method is available that provides an additional allowCrossPageConnection parameter for specifying explicitly whether an interface supports cross-page connections. The RegisterInterface method that does not include this parameter hard-codes the setting of the allowCrossPageConnection parameter to true for all connection interfaces that are supported by the connection compatibility rules defined by the Web Part infrastructure (see "Cross Page Connections," earlier in this topic). All other connection interfaces are hard coded to false.

Method parameters

Parameter Description
interfaceName A string property that is the friendly name of the interface. The friendly name should be unique within a part. This property cannot contain the following special characters: <, >, &, double quotation mark, single quotation mark, comma, or semicolon.
interfaceType A property that represents the type of the interface (IRowProvider, ICellConsumer, etc.).
maxConnections A property that the Web Part architecture can query to determine how many connections can be formed on a given interface. MaxConnections is an int. It can have a value of WebPart.LimitOneConnection or WebPart.UnlimitedConnections where WebPart.UnlimitedConnections = -1 WebPart.LimitOneConnection = 1
runAtOptions Defines where the interface can run (Client, Server, ClientAndServer, None).
interfaceObject A reference to the actual object that implements this interface.
interfaceClientReference For client-side connections, InterfaceClientReference is a string that will be used as the identifier for the client-side object, which implements that interface. This identifier should contain a _WPQ_ to guarantee uniqueness of the name, and it can contain special characters like (). An empty string can be used if the part does not support client-side communication. The Web Part architecture will encode double quotation marks as single quotation marks.

Note  _WPQ_ is a token that is replaced with a unique identifier by the Web Part architecture when a part is rendered.

menuLabel A general label or explanation of the interface. This will appear in the connection menu user interface. It is recommended that you start your menu label using a verb such as "Provide" or "Consume" so that the user creating a connection understands the direction of the flow of data.
description An extended description of the interface.

Example

InterfaceProperties myCellProviderInterface = RegisterInterface(
   "MyCellProviderInterface_WPQ_",   //InterfaceName
   "ICellProvider",         //InterfaceType
   WebPart.UnlimitedConnections,   //MaxConnections
   ConnectionRunAt.ServerAndClient,   //RunAtOptions
   this,               //InterfaceObject
   "CellProviderInterface_WPQ_",   //InterfaceClientReference
   "Provide Value From Text Box",         //MenuLabel
   "Provides the value entered into the text box.");   //Description

Note  To catch when the Connectionscode access security permission is not granted, you should enclose the RegisterInterface call in a try/catch block. For an example of how to do this, see the RegisterInterface method topic.

Step 4: Override the CanRunAt method

All connectable Web Parts must override the CanRunAt method. This method is called by the Web Part architecture to determine whether a Web Part can be run on the server, the client, or both. The Web Part needs to determine where it can run based on the current configuration of the user's client and the other Web Parts to which it is connected. The values that it can return are the following ConnectionRunAt enumeration values: Client, Server, ClientAndServer, and None. For example, a Web Part may have two different renderings: rich and downlevel. The Web Part may need an ActiveX component installed for its rich rendering. In this case, the CanRunAt method would return Client if the ActiveX component is installed or Server if it isn't installed.

Must Override? Yes

Example

public override ConnectionRunAt CanRunAt()
{
   // This Web Part can run on both the client and the server
   return ConnectionRunAt.ServerAndClient;
}

Step 5: Override the PartCommunicationConnect method

All connectable Web Parts must override the PartCommunicationConnect method. This method is called by the Web Part architecture to notify the Web Part that it has been connected, and to pass along relevant information such as which part it was connected to. This occurs as soon as the Web Part architecture links up the appropriate events for the connection. In this method, the Web Part author should keep track of where the interface can be run, create whatever UI controls are needed, and verify that the connection was formed correctly. This method has several parameters which are defined below.

Must Override? Yes

Method definition

public override void PartCommunicationConnect (string interfaceName,
                  WebPart connectedPart,
                  string connectedInterfaceName,
                  ConnectionRunAt runAt)

Method parameters

Parameter Description
interfaceName Friendly name of the interface that is being connected.
connectedPart Reference to the other Web Part that is being connected to. Both connectedPart and connectedInterfaceName parameters provide a means for the connecting part to identify the type of part that it is being connected to. This allows the Web Part to establish a more informed interaction with the other part. For example, if the source part has detailed knowledge of the target part, the source part can tap into the target part's public object model. However, both of these parts need to be designed with this intent for this to work correctly. For example, a chart part and pivot part could be designed to share the same layout of a common data source when they were connected.
connectedInterfaceName Friendly name of the interface on the other Web Part through which they are connected.
runAt Where the interface can be executed. This will be either the client or the server and is determined by the Web Part architecture based on several factors.

Example

public override void PartCommunicationConnect(string interfaceName,
   WebPart connectedPart,
   string connectedInterfaceName,
   ConnectionRunAt runAt)
{
   // Check to see if this is a client-side part
   if (runAt == ConnectionRunAt.Client)
   {
      // This is a client-side part
      _runAtClient = true;
      return;
   }

   // Must be a server-side part - create the Web Part's controls
   EnsureChildControls();

   // Check if this is my particular cell interface
   if (interfaceName == "MyCellProviderInterface_WPQ_")
   {
      // Keep a count of the connections
   _cellConnectedCount++;
   }
}

Step 6: Override the PartCommunicationInit method

A connectable Web Part can optionally override the PartCommunicationInit method. This method is called by the Web Part architecture to allow the Web Part to fire any initialization events. The Web Part should fire any events that end with "Init" (for example, CellProviderInit).

Init parameters are useful when a Web Part needs to broadcast some information about itself to other parts. For example, when a List part connects to a Form part, the List part could broadcast its field names. The Form part could then render its user interface based on the field names from the List part.

Must Override? Optional

Example

Note  For client-side Web Parts, this event and its event handler must be implemented on the client.

public override void PartCommunicationInit()
{
   //If the connection wasn't formed then don't send Init event
   if(_cellConnectedCount > 0)
   {
      //If there is a listener, send Init event
      if (CellProviderInit != null)
      {
         //Need to create the args for the CellProviderInit event
         CellProviderInitEventArgs cellProviderInitEventArgs = new
         CellProviderInitEventArgs();

         //Set the FieldName
         cellProviderInitEventArgs.FieldName = _cellName;
         cellProviderInitEventArgs.FieldDisplayName = _cellDisplayName;

         //Fire the CellProviderInit event
         CellProviderInit(this, cellProviderInitEventArgs);
      }
   }
}

Step 7: Override the PartCommunicationMain method

A connectable Web Part can optionally override the PartCommunicationMain method. This is called by the Web Part architecture to allow the Web Part to fire any of the other events from the interface (for example, CellReady). During the execution of the PartCommunicationMain method, the actual communication of data values (as opposed to schema) takes place between Web Parts.

Must Override? Optional

Example

Note  For client-side Web Parts, this event and its event handler must be implemented on the client.

public override void PartCommunicationMain()

{
   // NOTE: THIS CODE IS SPECIFIC TO EACH AND EVERY WEB PART'S IMPLEMENTATION.
   // If the connection wasn't formed then don't send Ready event
   if(_cellConnectedCount > 0)
   {
      // If there is a listener, send CellReady event
      if (CellReady != null)
      {
         // Need to create the args for the CellProviderInit event
         CellReadyEventArgs cellReadyEventArgs = new CellReadyEventArgs();

         // If user clicked button then send the value
         if (_cellClicked)
         {
            // Set the Cell to the value of the TextBox text
            // This is the value that will be sent to the Consumer
            cellReadyEventArgs.Cell = _cellInput.Text;
         }

         else
         {
            // The user didn't actually click the button
            // so just send an empty string to the Consumer
            cellReadyEventArgs.Cell = "";
         }

         // Fire the CellReady event
         // The Consumer will then receive the Cell value
         CellReady(this, cellReadyEventArgs);
      }
   }
}

Step 8: Override the GetInitEventArgs method

A connectable Web Part should override the GetInitEventArgs method if needed. The GetInitEventArgs method is only required for the interfaces that use transformers. For example, IRowProvider, ICellConsumer, IFilterConsumer, IParametersOutProvider, and IParametersInConsumer can support transformers. The GetInitEventArgs method is called by the Connection Authoring tools for all the initial data required to create the transformer user interface. The method returns the InitEventArgs object and takes in the interface name.

For example, when connecting two Web Parts that support the IRowProvider and ICellConsumer interfaces, the user needs to specify which field in the IRowProvider Web Part should map to the input value in the ICellConsumer Web Part. This is achieved by the Web Part architecture calling the GetInitEventArgs method on each of the interfaces. The Connection authoring tools such as FrontPage or the browser use the Init parameters passed to build the user interface for the transformer, which allows the user to pick the field mapping.

Must Override? Yes (if needed)

Method definition

public override InitEventArgs GetInitEventArgs(string interfaceName)

Method parameters

Parameter Description
interfaceName A string property that is the friendly name of the interface.

Example

Note  This method can be implemented on the server or client.

Important  A code example for a Web Part that implements the ICellProvider interface has been used as the example throughout these steps. However, the ICellProvider interface shouldn't override the GetInitEventArgs method because it can't use a transformer. However, for completeness, following is an example from the CellConsumer.cs code sample at the end of this programming task, which does override the GetInitEventArgs method.

public override InitEventArgs GetInitEventArgs(string interfaceName)
{
   //Check if this is my particular cell interface
   if (interfaceName == "MyCellConsumerInterface_WPQ_")
   {
      EnsureChildControls();

      //Need to create the args for the CellConsumerInit event
      CellConsumerInitEventArgs cellConsumerInitArgs =
         new CellConsumerInitEventArgs();

      //Set the FieldName and FieldDisplayName
      cellConsumerInitArgs.FieldName = _cellName;
      cellConsumerInitArgs.FieldDisplayName = _cellDisplayName;

      //return the InitArgs
      return(cellConsumerInitArgs);
   }
   else
   {
      return(null);
   }
}

Step 9: Implement the interface event handlers

Implement the appropriate event handlers based on the type of interface being used. In this example, the ICellProvider interface must implement the CellConsumerInitEventHandler event handler. This event handler must be implemented whether the data passed by the ICellConsumer interface is used or not. The consumer part will fire this event when its PartCommunicationInit method runs.

Method defintion

public void CellConsumerInit(object sender, CellConsumerInitEventArgs cellConsumerInitEventArgs)

Method parameters

Parameter Description
sender The object calling this method.
cellConsumerInitEventArgs Parameters passed by the consumer Web Part during the PartCommunicationInit phase.

Example

public void CellConsumerInitEventHandler(object sender, CellConsumerInitEventArgs cellConsumerInitEventArgs)
{
   // This is where the Provider part could see what type of "Cell"
   //  the Consumer was expecting/requesting.
   // For this simple code example, this information is not used
   // anywhere.

}

Step 10: Override the RenderWebPart method

All Web Parts must override the RenderWebPart method. The details of this are specific to each and every Web Part. The Web Part infrastructure calls this method to render the Web Part. For a full example, see the CellProvider.cs source code located at end of this programming task topic. For brevity, the following provides a skeleton sample.

Example

protected override void RenderWebPart(HtmlTextWriter output)
{
   // Need to ensure that all of the Web Part's controls are created
   EnsureChildControls();

   // Render client connection code if the connection is client-side
   if (_runAtClient)
   {
      // Script for client-side rendering
   }
   else
   {
      // If connected then display all cell child controls
      if (_cellConnectedCount > 0)
      {
         // Code for server-side rendering
      }

      else
      {
         // There wasn't a cell connection formed,
      }
   }
}

Step 11: Implement supporting methods

At this location in the ICellProvider source code, all supporting methods should be defined. A skeleton is shown below. For a full example, see the CellProvider.cs source code located at end of this programming task topic.

Example

// Create all of the controls for this Web Part
protected override void CreateChildControls()
{
   //Code for Child Controls
}

// The Button OnClick event handler
private void CellButtonClicked(object sender, EventArgs e)
{
   _cellClicked = true; //user clicked button, set to true
}

A pair of sample Web Parts that implement the ICellProvider and ICellConsumer interfaces

The following two code samples illustrate how to create two connectable Web Parts that implement the ICellProvider and ICellConsumer interfaces.

To complete this programming task  

  • Cut and paste the following code samples into two C# files of your Web Part project, and then build your project.
//--------------------------------------------------------------------
// File : CellProvider.cs
//
// Purpose : A sample connectable Web Part that implements the
//          ICellProvider interface.
//
//---------------------------------------------------------------------

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebPartPages.Communication;
using System.Runtime.InteropServices;

namespace ICellDemo
{
    /// <summary>
    /// The CellProvider Web Part class implementes the ICellProvider
    /// interface. Its UI is very basic - it displays a simple text
    /// box and button.  The CellConsumer Web Part class implements
    /// the ICellConsumer interface.  When the CellProvider is
    /// connected to the CellConsumer on a Web Part Page, the CellProvider
    /// can pass the value in its text box to the CellConsumer which displays
    /// the value inline.
    /// </summary>
    ///

    //Step #1: Implement the Connection Interface (ICellProvider)
    public class CellProvider : WebPart, ICellProvider
    {
        //Step #2: Declare Connection Events
        public event CellProviderInitEventHandler CellProviderInit;
        public event CellReadyEventHandler CellReady;

        //Used to keep track of whether or not the connection will be running client-side
        private bool _runAtClient = false;

        //Keep a count of ICell connections
        private int _cellConnectedCount = 0;

        //Web Part UI
        private Button _cellButton; 
        private TextBox _cellInput;
        
        //Cell information
        private string _cellName;
        private string _cellDisplayName;

        //Used to keep track of whether or not the Button in the Web Part was clicked
        private bool _cellClicked = false;


        //Step #3: EnsureInterfaces
        //Notification to the Web Part that is should ensure that all
        //its interfaces are registered using RegisterInterface.
        public override void EnsureInterfaces()
        {
            //Registers an interface for the Web Part
            RegisterInterface("MyCellProviderInterface_WPQ_",  //InterfaceName
                InterfaceTypes.ICellProvider,                   //InterfaceType
                WebPart.UnlimitedConnections,                   //MaxConnections
                ConnectionRunAt.ServerAndClient,                //RunAtOptions
                this,                                           //InterfaceObject
                "CellProviderInterface_WPQ_",                   //InterfaceClientReference
                "Provide String from Textbox",                  //MenuLabel
                "Provides a Textbox string");                   //Description
        }


        //Step #4: CanRunAt - called by framework to determine where a part can run.
        public override ConnectionRunAt CanRunAt()
        {
            //This Web Part can run on both the client and the server
            return ConnectionRunAt.ServerAndClient;
        }

        //Step #5: PartCommunicationConnect - Notification to the Web Part that it has been connected.
        public override void PartCommunicationConnect(string interfaceName,
            WebPart connectedPart,
            string connectedInterfaceName,
            ConnectionRunAt runAt)
        {
            //Check to see if this is a client-side part
            if (runAt == ConnectionRunAt.Client)
            {
                //This is a client-side part
                _runAtClient = true;
                return;
            }
            
            //Must be a server-side part so need to create the Web Part's controls
            EnsureChildControls();

            //Check if this is my particular cell interface
            if (interfaceName == "MyCellProviderInterface_WPQ_")
            {
                //Keep a count of the connections
                _cellConnectedCount++;
            }
        }

        //Step #6: PartCommunicationInit - Notification to the Web Part that it has been connected.
        public override void PartCommunicationInit()
        {
            //If the connection wasn't actually formed then don't want to send Init event
            if(_cellConnectedCount > 0)
            {
                //If there is a listener, send Init event
                if (CellProviderInit != null)
                {
                    //Need to create the args for the CellProviderInit event
                    CellProviderInitEventArgs cellProviderInitArgs = new CellProviderInitEventArgs();
                    
                    //Set the FieldName
                    cellProviderInitArgs.FieldName = _cellName;
                    cellProviderInitArgs.FieldDisplayName = _cellDisplayName;
                    
                    //Fire the CellProviderInit event.
                    CellProviderInit(this, cellProviderInitArgs);
                }
            }
        }

        //Step #7: PartCommunicationMain - Called by the framework to allow part to fire any remaining events
        public override void PartCommunicationMain()
        {
            //If the connection wasn't actually formed then don't want to send Ready event
            if(_cellConnectedCount > 0)
            {
                //If there is a listener, send CellReady event
                if (CellReady != null)
                {
                    //Need to create the args for the CellProviderInit event
                    CellReadyEventArgs cellReadyArgs = new CellReadyEventArgs();

                    //If user clicked button then send the value
                    if (_cellClicked)
                    {
                        //Set the Cell to the value of the TextBox text
                        //This is the value that will be sent to the Consumer
                        cellReadyArgs.Cell = _cellInput.Text;
                    }
                    else
                    {
                        //The user didn't actually click the button
                        //so just send an empty string to the Consumer
                        cellReadyArgs.Cell = "";
                    }

                    
                    //Fire the CellReady event.
                    //The Consumer will then receive the Cell value
                    CellReady(this, cellReadyArgs);
                }
            }
        }

        //Step #8: GetInitArgs is not needed in this case. GetInitEventArgs only needs to be
        //implemented for interfaces that can participate in a transformer which are
        //the following: ICellConsumer, IRowProvider, IFilterConsumer, IParametersOutProvider,
        //IParametersInConsumer

        //Step #9: Implement CellConsumerInit event handler.
        public void CellConsumerInit(object sender, CellConsumerInitEventArgs cellConsumerInitArgs)
        {
            //This is where the Provider part could see what type of "Cell" the Consumer
            //was expecting/requesting.
            //For this simple code example, this information is not used anywhere.
        }

        //Step #10: RenderWebPart - defines Web Part UI and behavior
        protected override void RenderWebPart(HtmlTextWriter output)
        {
            //Need to ensure that all of the Web Part's controls are created
            EnsureChildControls();

            //Render client connection code if the connection is client-side
            if (_runAtClient)
            {
                //Connected client-side
                output.Write(ReplaceTokens("<br><h5>Connected Client-Side</h5><br>\n"
                    + "<input type=\"text\" id=\"CellInput_WPQ_\"/>\n"
                    + "<button id=\"CellButton_WPQ_\" onclick=\"CellButtonOnClick_WPQ_()\">Fire CellReady</button>\n"
                    + "<SCRIPT LANGUAGE=\"JavaScript\">\n"
                    + "<!-- \n"
                    + "    var CellProviderInterface_WPQ_ = new myCellProviderInterface_WPQ_();\n"

                    + "    function myCellProviderInterface_WPQ_()\n"
                    + "    {\n"
                    + "        this.PartCommunicationInit = myInit;\n"
                    + "        this.PartCommunicationMain = myMain;\n"
                    + "        this.CellConsumerInit = myCellConsumerInit;\n"

                    + "        function myInit()\n"
                    + "        {\n"
                    + "            var cellProviderInitArgs = new Object();\n"
                    + "            cellProviderInitArgs.FieldName = \"CellName\";\n"

                    + "            WPSC.RaiseConnectionEvent(\"MyCellProviderInterface_WPQ_\", \"CellProviderInit\", cellProviderInitArgs);\n"
                    + "        }\n"

                    + "        function myMain()\n"
                    + "        {\n"
                    + "            var cellReadyArgs = new Object();\n"
                    + "            cellReadyArgs.Cell = \"\";\n"

                    + "            WPSC.RaiseConnectionEvent(\"MyCellProviderInterface_WPQ_\", \"CellReady\", cellReadyArgs);\n"
                    + "        }\n"

                    + "        function myCellConsumerInit(sender, cellConsumerInitArgs)\n"
                    + "        {\n"
                    + "        }\n"
                    + "    }\n"

                    + "    function CellButtonOnClick_WPQ_()\n"
                    + "    {\n"
                    + "        var cellReadyArgs = new Object();\n"
                    + "        cellReadyArgs.Cell = document.all(\"CellInput_WPQ_\").value;\n"

                    + "        WPSC.RaiseConnectionEvent(\"MyCellProviderInterface_WPQ_\", \"CellReady\", cellReadyArgs);\n"
                    + "    }\n"
                    + "//-->\n"
                    + "</SCRIPT>"));
            }
            else //Connected server-side
            {
                //If connected then display all cell child controls
                if (_cellConnectedCount > 0)
                {
                    //Just render some informational text
                    output.RenderBeginTag(HtmlTextWriterTag.Br);
                    output.RenderEndTag();
                    output.RenderBeginTag(HtmlTextWriterTag.H5);
                    output.Write("Connected Server-Side");
                    output.RenderEndTag();
                    output.RenderBeginTag(HtmlTextWriterTag.Br);
                    output.RenderEndTag();

                    //Render the TextBox control
                    _cellInput.RenderControl(output);

                    //Render the Button
                    _cellButton.RenderControl(output);
                }
                else
                {
                    //There wasn't a cell connection formed,
                    //so just output a message
                    output.Write("NO CELL INTERFACE CONNECTION");
                }
            }
        }

        //Step #11.1 (Supporting Methods): CreateChildControls
        protected override void CreateChildControls()
        {
            //Create the Button
            _cellButton = new Button();
            _cellButton.ID = "CellButton";
            _cellButton.Text = "Fire CellReady";
            Controls.Add(_cellButton);
        
            //Create the TextBox
            _cellInput = new TextBox();
            _cellInput.ID = "CellInput";
            Controls.Add(_cellInput);

            //Set the Cell information.
            //This information will be passed to the Consumer by
            //firing the CellProviderInit event.
            _cellName = "CellInput";
            _cellDisplayName = "CellDisplayInput";
    
            _cellClicked = false; // Initialize to false -- user hasn't clicked yet
            _cellButton.Click += new EventHandler(CellButtonClicked); // listen for Button's click event
        }

        //Step #11.2 (Supporting Methods): CellButtonClicked
        // <param name="sender">The Button object</param>
        // <param name="e">The Event Arguments</param>
        private void CellButtonClicked(object sender, EventArgs e)
        {
            _cellClicked = true; //user clicked button, set to true
        }
    }
}

//--------------------------------------------------------------------
// File : CellConsumer.cs
//
// Purpose : A sample connectable Web Part that implements the
//          ICellConsumer interface.
//
//---------------------------------------------------------------------
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebPartPages.Communication;
using System.Runtime.InteropServices;

namespace ICellDemo
{

    //Step #1: Implement the Connection Interface (ICellConsumer)
    public class CellConsumer : WebPart, ICellConsumer
    {
        
        //Step #2: Declare Connection events
        public event CellConsumerInitEventHandler CellConsumerInit;

        //Used to keep track of whether or not the connection will be running client-side
        private bool _runAtClient = false;

        //Keep a count of ICell connections
        private int _cellConnectedCount = 0;

        //Web Part UI
        private Label _cellLabel;

        //Cell information
        private string _cellName;
        private string _cellDisplayName;

        //Step #3: EnsureInterfaces
        //Notification to the Web Part that is should ensure that all
        //its interfaces are registered using RegisterInterface.
        public override void EnsureInterfaces()
        {
            //Registers an interface for the Web Part.
            RegisterInterface("MyCellConsumerInterface_WPQ_",   //InterfaceName
                InterfaceTypes.ICellConsumer,                   //InterfaceType
                WebPart.UnlimitedConnections,                   //MaxConnections
                ConnectionRunAt.ServerAndClient,                //RunAtOptions
                this,                                           //InterfaceObject
                "CellConsumerInterface_WPQ_",                   //InterfaceClientReference
                "Get String Value",                             //MenuLabel
                "Just a simple ICellConsumer");                 //Description
        }

        //Step #4: CanRunAt - called by framework to determine where a part can run.
        public override ConnectionRunAt CanRunAt()
        {
            //This Web Part can run on both the client and the server
            return ConnectionRunAt.ServerAndClient;
        }

        //Step #5: PartCommunicationConnect - Notification to the Web Part that it has been connected.
        public override void PartCommunicationConnect(string interfaceName,
            WebPart connectedPart,
            string connectedInterfaceName,
            ConnectionRunAt runAt)
        {
            //Check to see if this is a client-side part
            if (runAt == ConnectionRunAt.Client)
            {
                //This is a client-side part
                _runAtClient = true;
                return;
            }
            
            //Must be a server-side part so need to create the Web Part's controls
            EnsureChildControls();

            //Check if this is my particular cell interface
            if (interfaceName == "MyCellConsumerInterface_WPQ_")
            {
                //Keep a count of the connections
                _cellConnectedCount++;
            }
        }

        //Step #6: PartCommunicationInit - Notification to the Web Part that it has been connected.
        public override void PartCommunicationInit()
        {
            //If the connection wasn't actually formed then don't want to send Init event
            if(_cellConnectedCount > 0)
            {
                //If there is a listener, send init event
                if (CellConsumerInit != null)
                {
                    //Need to create the args for the CellConsumerInit event
                    CellConsumerInitEventArgs cellConsumerInitArgs = new CellConsumerInitEventArgs();
                    
                    //Set the FieldNames
                    cellConsumerInitArgs.FieldName = _cellName;

                    //Fire the CellConsumerInit event.
                    //This basically tells the Provider Web Part what type of
                    //cell the Consuemr is expecting in the CellReady event.
                    CellConsumerInit(this, cellConsumerInitArgs);
                }
            }
        }

        //Step #7: PartCommunicationMain - this method doesn't need to be implemented for the Consumer
        //because the Consumer doesn't have any events that need to be fired during this phase.


        //Step #8: GetInitArgs - called by the connection authoring tool, e.g., browser or FrontPage
        //to get the data required to build the transformer UI.
        public override InitEventArgs GetInitEventArgs(string interfaceName)
        {
            //Check if this is my particular cell interface
            if (interfaceName == "MyCellConsumerInterface_WPQ_")
            {
                EnsureChildControls();

                //Need to create the args for the CellConsumerInit event
                CellConsumerInitEventArgs cellConsumerInitArgs = new CellConsumerInitEventArgs();
                    
                //Set the FieldName
                cellConsumerInitArgs.FieldName = _cellName;
                cellConsumerInitArgs.FieldDisplayName = _cellDisplayName;
                    
                //return the InitArgs
                return(cellConsumerInitArgs);
            }
            else
            {
                return(null);
            }
        }

        
    
        //Step #9.1: Implement CellProviderInit Event Handler.
        public void CellProviderInit(object sender, CellProviderInitEventArgs cellProviderInitArgs)
        {
            //This is where the Consumer part could see what type of "Cell" the Provider
            //will be sending.
            //For this simple code example, this information is not used anywhere.
        }

        //Step #9.2: Implement CellReady Event Handler.
        //Set label text based on value from the CellProvider Web Part
        public void CellReady(object sender, CellReadyEventArgs cellReadyArgs)
        {
            //Set the label text to the value of the "Cell" that was passed by the Provider
            if(cellReadyArgs.Cell != null)
            {
                _cellLabel.Text = cellReadyArgs.Cell.ToString();
            }
        }

        //Step #10: RenderWebPart - defines Web Part UI and behavior
        protected override void RenderWebPart(HtmlTextWriter output)
        {
            //Need to ensure that all of the Web Part's controls are created
            EnsureChildControls();

            //Render client connection code if needed
            if (_runAtClient)
            {
                //Connected client-side
                string strClientCode = "<br><h5>Connected Client-Side</h5><br>\n";
                strClientCode += "<div id=\"ConsumerDiv_WPQ_\"/>\n";
                strClientCode += "<SCRIPT LANGUAGE=\"JavaScript\">\n";
                strClientCode += "<!-- \n";
                strClientCode += "    var CellConsumerInterface_WPQ_ = new myCellConsumerInterface_WPQ_();\n";

                strClientCode += "    function myCellConsumerInterface_WPQ_()\n";
                strClientCode += "    {\n";
                strClientCode += "        this.PartCommunicationInit = myInit;\n";
                strClientCode += "        this.CellProviderInit = myCellProviderInit;\n";
                strClientCode += "        this.CellReady = myCellReady;\n";

                strClientCode += "        function myInit()\n";
                strClientCode += "        {\n";
                strClientCode += "            var cellConsumerInitArgs = new Object();\n";
                strClientCode += "            cellConsumerInitArgs.FieldName = \"CellName\";\n";

                strClientCode += "            WPSC.RaiseConnectionEvent(\"MyCellConsumerInterface_WPQ_\", \"CellConsumerInit\", cellConsumerInitArgs);\n";
                strClientCode += "        }\n";

                strClientCode += "        function myCellProviderInit(sender, cellProviderInitArgs)\n";
                strClientCode += "        {\n";
                strClientCode += "        }\n";

                strClientCode += "        function myCellReady(sender, cellReadyArgs)\n";
                strClientCode += "        {\n";
                strClientCode += "            document.all('ConsumerDiv_WPQ_').innerHTML = cellReadyArgs.Cell;\n";
                strClientCode += "        }\n";

                strClientCode += "    }\n";
                strClientCode += "//-->\n";
                strClientCode += "</SCRIPT>";

                output.Write(ReplaceTokens(strClientCode));
            }
            else //Connected server-side
            {
                //If we are connected then display all child controls
                if (_cellConnectedCount > 0)
                {           
                    //Just render some informational text
                    output.RenderBeginTag(HtmlTextWriterTag.Br);
                    output.RenderEndTag();
                    output.RenderBeginTag(HtmlTextWriterTag.H5);
                    output.Write("Connected Server-Side");
                    output.RenderEndTag();
                    output.RenderBeginTag(HtmlTextWriterTag.Br);
                    output.RenderEndTag();

                    //Render the Label control
                    _cellLabel.RenderControl(output);
                }
                else
                {
                    //else display no connection message
                    output.Write("NO CELL INTERFACE CONNECTION");
                }
            }           
        }

        //Step #11.1 (Supporting Methods): CreateChildControls
        protected override void CreateChildControls()
        {
            //Create the Label
            _cellLabel = new Label();
            _cellLabel.ID = "CellLabel";
            Controls.Add(_cellLabel);

            //Set the Cell information.
            //This information will be passed to the Provider by
            //firing the CellConsumerInit event.
            _cellName = "CellInputabc";
            _cellDisplayName = "My CellInput";
        }
    }
}

To deploy and test these sample Web Parts  

  • Perform the steps described in the "Deploying Your Web" section of the Creating a Basic Web Part programming task, making sure to create a SafeControl block and Web Part Definition file (.dwp) that reflects the appropriate values for your Web Part assembly.
  • After importing the sample Web Parts into a Web Part Page, connect the Web Parts:
    • Click Modify Shared Page (or Modify My Page), and then click Design This Page.
    • Click the Web Part Menu on the Cell Consumer Web Part, point to Connections, point to Consume Cell from, and then click Cell Provider Web Part.
    • In the Cell Provider Web Part, enter a value into the text box, and then click the Fire CellReady button.