Building Connectable Web Parts


Building Connectable Web Parts

The philosophy behind the use of web parts in SharePoint Portal Server (SPS) is that end users should be able to access information and assemble views without having to rely upon programmers to create custom web pages. One of the ways that this philosophy is put into action is through the use of web part connections. Connecting web parts in the portal allows a value from one web part to be used as an input, sort , or filter for the display of another web part.

Earlier in the book, you saw this functionality from the end- user perspective. In that example, you created a master-detail view of a contact list by using one web part to select a contact name and a second web part to display the detailed contact information. One of the main uses of connected web parts is creating these types of master-detail views, which allows end users to customize how information appears on their portal pages.

Behind the scenes, SPS uses the web part infrastructure to determine which web parts on a page are suitable for connection. Connectable web parts are then given a special Connections item on their drop-down menu that lists all of the other web parts to which it can connect. If you want to create connectable web parts that can be used in SPS, you must understand how to integrate your web parts with the connection infrastructure.

Connection Interfaces

The primary mechanism for integrating web parts with the connection infrastructure is through a set of interfaces. These interfaces expose methods and events that allow the connection infrastructure to query your web parts for appropriate connection information and provide notification when another web part wants to connect. The available interfaces support passing a single piece of data, a row of data, an entire list of data, or custom data sets between web parts. Table 7-1 lists the available interfaces and their purposes.

Table 7-1: Connection Interfaces

INTERFACE

PURPOSE

ICellProvider

Implemented by a web part that can provide a single value to other web parts

ICellConsumer

Implemented by a web part that can consume a single value from other web parts

IRowProvider

Implemented by a web part that can provide an entire row of data to other web parts

IRowConsumer

Implemented by a web part that can consume an entire row of data from other web parts

IListProvider

Implemented by a web part that can provide an entire list to other web parts

IListConsumer

Implemented by a web part that can consume an entire list from other web parts

IFilterProvider

Implemented by a web part that can provide a value for filtering to other web parts

IFilterConsumer

Implemented by a web part that can use a provided value from other web parts for filtering a view

IParametersInProvider

Implemented by a web part that can provide arbitrary input values to other web parts

IParametersInConsumer

Implemented by a web part that can consume arbitrary input values from other web parts

IParametersOutProvider

Implemented by a web part that can provide arbitrary output values to other web parts

IParametersOutConsumer

Implemented by a web part that can consume arbitrary output values from other web parts

Connection interfaces are provided in complementary pairs that can be implemented to pass data such as ICellProvider and ICellConsumer . However, connection interfaces can often allow connections that are not immediately obvious. For example, a web part that provides an entire row can be connected to a web part that only consumes a single field. This is because the web part infrastructure implements a selection dialog that allows end users to select which field from the row will be consumed. This means that there are many possible combinations of compatible interfaces. Figure 7-1 shows a typical field selection dialog in SPS.

click to expand
Figure 7-1: Connecting web parts in SPS

Determining which interfaces are compatible is handled by the web part infrastructure according to several rules. The first, and most obvious, rule is that all complementary interface pairs are compatible. This means that ICellProvider/ICellConsumer , IRowProvider/IRowConsumer , and IListProvider/IListConsumer are always compatible. For interfaces that are not complementary, extended connections ”known as transformers ”are allowed where they make sense; however, some of these connections are not supported directly in SPS and can only be achieved when you are editing the page in Microsoft FrontPage. Table 7-2 lists these interfaces and their restrictions.

Table 7-2: Extended Connection Compatibility

IFILTERPROVIDER

IROWPROVIDER

IPARAMETERS-INPROVIDER

IPARAMETERS-OUTPROVIDER

ICellConsumer

SPS [1] /FP [2]

IFilterConsumer

CPC [3]

SPS/FP/CPC

IParametersIn-Consumer

FP/CPC

FP/CPC

FP/CPC

[1] SPS: Connection creation allowed directly in SPS

[2] FP: Connection creation allowed in Microsoft FrontPage

[3] CPC: Cross-page connections allowed in Microsoft FrontPage

During the design of your web part, you determine the interfaces to implement based on its intended use. Keep in mind that your web part must be easily understood by portal end users. Your goal is to avoid the need for detailed training or help files associated with your web part. To the greatest extent possible, the purpose of your web part should be understood through its display and the options provided on the connection menu.

Once you have determined which interfaces will be implemented by your web part, you are ready to begin development. You can start your web part using the same web part templates that you used in earlier chapters. Although the web part templates have some specific templates available just for connectable web parts, they are generally geared toward simple single-value connections. You will find them lacking if you want to create more sophisticated web parts.

Regardless of how you start the project, you must specify the interfaces to implement in your web part. All of the interfaces for connecting web parts are located in the Microsoft.SharePoint.WebPartPages.Communication namespace. Declaring that a class implements an interface from this namespace requires that every method and event in the interface be declared. Each of the interfaces available for connecting web parts has a some-what differing set of events and methods; therefore, you should be careful with the declarations. Listing 7-2 shows an example of declaring the IRowProvider interface in VB.NET.

Listing 7-2: Declaring Interfaces
start example
 Imports Microsoft.SharePoint.WebPartPages.Communication <DefaultProperty("Text"), ToolboxData("<{0}:WebPart1 runat=server></{0}:WebPart1>"), XmlRoot(Namespace:="SPSDataSet")> _ Public Class WebPart1 Inherits Microsoft.SharePoint.WebPartPages.WebPart     Implements IRowProvider     Public Event RowProviderInit(ByVal sender As Object, ByVal e As Microsoft.SharePoint.WebPartPages.Communication.RowProviderInitEventArgs) Implements Microsoft.SharePoint.WebPartPages. _ Communication.IRowProvider.RowProviderInit     Public Event RowReady(ByVal sender As Object, ByVal e As Microsoft.SharePoint.WebPartPages.Communication.RowReadyEventArgs) Implements Microsoft.SharePoint.WebPartPages. _ Communication.IRowProvider.RowReady End Class 
end example
 

Connection Life Cycle

Correctly implementing the interfaces to support communication is a painstaking process that you need to understand thoroughly to be successful. Each of the methods and events you must code are directly connected to the process used by the web part framework to connect the target web parts. Before you begin development, you need to examine the sequence of events that happen when two web parts are connected.

Consider the scenario in which two web parts are on a page in SPS but are not yet connected. Assume that the web parts have implemented complementary interfaces. The exact interfaces are not critical to the discussion, so I will simply refer to the web parts as the provider part and the consumer part.

The connection process begins when the end user selects to connect the provider and consumer using the drop-down menu associated with either web part. When this happens, the web part infrastructure responds by querying both the provider and consumer web parts to get a reference to interfaces they implement. This information allows the web part infrastructure to begin using the interfaces to create the connection.

Once the web part infrastructure has access to the interfaces, the next thing it does is ask the web parts whether they support connecting on the client, the server, or both. This information is provided to the connecting web parts so that they can correctly prepare for the connection.

Once the web part architecture determines where the web parts run, it connects the web parts. Each web part is notified that the connection has taken place and is passed relevant information regarding the pending data transfer. This way each of the web parts can react to the connection and prepare for the transaction.

Once the web parts are connected, the infrastructure instructs the web parts to fire any preparatory events. Typically, these events involve broadcasting schema information regarding the transfer to the other web part. The provider part web part might broadcast a list of field names that represent the columns in a row, or it may simply send a single field name associated with a cell depending upon the implemented interface. For its turn , the consumer part will broadcast similar schema information to specify what data it is expecting to receive.

At this point in the process, the provider web part is waiting for some user interaction that will signal the start of a transfer. Generally, this involves the selection of an item or row. Such a selection causes the web part infrastructure to notify the provider part that the data transfer has begun. The provider part then fires an event within the consumer part that sends the selected data. When the consumer part receives the data, it responds by modifying its view in accordance with its designed functionality. Once the transfer of data is complete, the web part infrastructure redraws both web parts. Figure 7-2 shows a diagram of the connection life cycle.

click to expand
Figure 7-2: The connection life cycle

Each of the steps in the connection life cycle is associated with a method or event in the interface implemented by a web part. The process of creating connectable web parts is one of coding the methods and events to achieve the correct functionality. As an example, we'll investigate the simplest form of data transfer ”a single field. A single field can be transferred using the complementary interfaces ICellProvider and ICellConsumer .

Registering Interfaces

Before connections can be made between web parts, the web part infrastructure must know what interfaces are implemented by each web part. Using this information, the web part infrastructure can ensure that only compatible web parts are connected. This prevents end users from making connection errors that could cause strange behavior in the portal.

Web parts tell the infrastructure about the interfaces they support by overriding the EnsureInterfaces method. EnsureInterfaces is a member of the WebPart class and is called by the infrastructure whenever it needs updated information regarding supported interfaces. Within this method, web parts make a call to the RegisterInterface method for each interface they support regardless of whether the interface is a provider or a consumer. Table 7-3 lists the parameters for the RegisterInterface method.

Table 7-3: RegisterInterface Parameters

PARAMETER

TYPE

DESCRIPTION

InterfaceName

String

A friendly name for the interface. This name should be unique within the web part and not contain any special characters (e.g., MyInterface ).

InterfaceType

String

The text name of the interface (e.g., ICellProvider , ICellConsumer ).

MaxConnections

Enumeration

The parameter that specifies that the web part can connect to only one web part ( WebPart.LimitOneConnection ) or any number of parts ( WebPart.UnlimitedConnections ).

RunAt

Enumeration

The parameter that specifies whether data is transferred on the client ( ConnectionRunAt.Client ), the server ( ConnectionRunAt.Server ), or both ( ConnectionRunAt.ServerAndClient ).

InterfaceObject

Object

A reference to the object that implements this interface (typically Me or this ).

ClientReference

String

A unique identifier used only for client connections. This name should contain the token _WPQ_, which is replaced at connection time with a guaranteed unique identifier.

MenuItem

String

The text that will appear in the connection menu.

Description

String

A description of the interface.

The ability to register an interface for the purpose of connecting web parts is subject to code access security requirements. By default, web part connections are supported in both the WSS_Minimal and WSS_Medium policies. If you use a custom policy, however, you will have to add the permission as we discussed in Chapter 5. Because of the potential for an error, you should call the RegisterInterface method inside of a try/catch block and trap for the SecurityException class. Listing 7-3 shows an example of calling the RegisterInterface method using C#.

Listing 7-3: Registering an Interface
start example
 public override void EnsureInterfaces() {     try     {         RegisterInterface("MyInterface",             "ICellConsumer",             WebPart.UnlimitedConnections,             ConnectionRunAt.Server,             this,             "",             "Get a company identifier from...",             "Receives a company identifier");     }     catch(SecurityException e)     {         //Must implement "WSS_Minimal" or "WSS_Medium"         //Show exception message in a label         lblMessage.Text += e.Message + "<br>";     } } 
end example
 

Running on Client or Server

Once the web parts have notified the infrastructure that they are connectable, they must specify whether they can connect on the server, the client, or both. All web parts, regardless of the particular interfaces they implement, must provide this information. The infrastructure queries the web part by calling the CanRunAt method. The web part then returns one of the enumerated values ConnectionRunAt.Client , ConnectionRunAt.Server , or ConnectionRunAt.ServerAndClient . The following code shows an example in VB.NET.

 Public Overrides Function CanRunAt() As ConnectionRunAt     Return ConnectionRunAt.Server End Function 

Although the code above is quite simple, some situations may require more processing. For example, pages with an ActiveX component installed for client processing may switch to server processing if the control is not installed.

Connection Notifications

Once the web part infrastructure understands where to connect the parts and on what interfaces, the connection is made. Both the provider and consumer web parts are notified that the connection has been established through a call to the PartCommunicationConnect method. This method passes along relevant information that each web part may care to track including a reference to the other web part, the interface that is connected, and where the data transfer will occur. Table 7-4 lists the arguments of the PartCommunicationConnect method.

Table 7-4: PartCommunicationConnect Arguments

ARGUMENT

TYPE

DESCRIPTION

InterfaceName

String

A friendly name for the interface. This should be the same as the value you provided in the RegisterInterfaces method.

ConnectedPart

WebPart

A reference to the other web part in the connection.

ConnectedInterfaceName

String

The friendly name of the interface on the other web part in the connection.

RunA t

Enumeration

Specifies where the data transfer will take place.

When the PartCommunicationConnect method is called, your web part should validate all of the information that it receives. This includes checking to see if the friendly interface name sent in is the same as the one that was sent out when RegisterInterfaces was called. Additionally, you should call EnsureChildControl to force the CreateChildControls method to run. This ensures that your user interface is ready to respond to the data transaction. Listing 7-4 shows an example of coding the PartCommunicationConnect method in VB.NET.

Listing 7-4: Receiving Connection Notification
start example
 Public Overrides Sub PartCommunicationConnect(_ ByVal InterfaceName As String, ByVal connectedPart As _ Microsoft.SharePoint.WebPartPages.WebPart, _ ByVal connectedInterfaceName As String, ByVal runAt As _ Microsoft.SharePoint.WebPartPages.Communication.ConnectionRunAt) 'Purpose: To inform this web part that the infrastructure has connected it to 'another part     'This part only connects on the server     If runAt = ConnectionRunAt.Server Then         'Add the child controls for the part         EnsureChildControls()         'Increment the connection counter         If InterfaceName = MyInterfaceName Then             intConnectionCount += 1         End If     End If End Sub 
end example
 

Broadcasting Schema Information

Once the connection is made, each part is allowed to broadcast relevant schema information to the other part. This broadcast functions to allow each web part to receive more detailed information about the data before it is transferred. Typically this schema information includes one or more field names that identify the data to be transferred. Web parts can use this information to validate the expected data before the transaction begins.

The web part infrastructure starts the broadcasting process by calling the PartCommunicationInit method on each web part involved in the connection. When a web part receives this call, it then executes specific initialization events that broadcast the information to interested listeners. The listeners may then take any necessary action to prepare for the pending data transfer based on the schema information sent.

Up to this point, your web parts have behaved largely identically regardless of whether they were providers or consumers. When it comes to broadcasting initialization events prior to the actual data transfer, however, each web part has its own custom events. This means that the implementation of the PartCommunicationInit method will be different in each web part.

Although the behavior of each web part will vary, Microsoft engineers have followed a convention that dictates events ending with the Init suffix are candidates for firing in the PartCommunicationInit method. This convention makes it easier to decide how to code the method. Listing 7-5 shows an example of a web part that implements ICellConsumer that broadcasts schema information via the CellConsumerInit event.

Listing 7-5: Broadcasting Schema Information
start example
 public override void PartCommunicationInit() {     if(m_connectionCount > 0)     {         CellConsumerInitEventArgs initArgs = new CellConsumerInitEventArgs();         initArgs.FieldName = myCellName;         initArgs.FieldDisplayName = myCellTitle;         CellConsumerInit(this, initArgs);     } } 
end example
 

In many simple web parts, the broadcasting of schema information adds little value. If, for example, a web part can only accept a Company Name field, it will be powerless to do anything if it is connected to a Customer Name field instead. Because these situations are possible, it is important to validate the schema information, but also to provide sufficient error handling to deal with meaningless values when they are received. Often this is simply a matter of showing no results in the consumer web part until a valid value is sent by the provider web part.

Exchanging Data

Once the web parts have broadcast their schema information, they are ready for the actual data exchange. The web part infrastructure initiates this exchange by calling the PartCommunicationMain method. This method allows web parts to fire any other events that are necessary to complete the transaction.

Although it is possible for both a provider and consumer web part to fire events from the PartCommunicationMain method, most often you will use it in a provider part to send the actual data to the consumer part. Following the event naming convention, any event that does not end with the Init suffix is a candidate for firing in PartCommunicationMain . Listing 7-6 shows how a web part implementing ICellProvider sends its data by firing the CellReady event and passing the selected value from a ListBox control.

Listing 7-6: Sending Data
start example
 Public Overrides Sub PartCommunicationMain()     Dim objReadyArgs As CellReadyEventArgs = New CellReadyEventArgs     'Make sure we are connected and have a selected item in the list     If intConnectionCount > 0 And lstCompanies.SelectedIndex <> -1 Then         'Set the field value         objReadyArgs.Cell = lstCompanies.SelectedItem.Text         'Fire the CellReady event to send the data         RaiseEvent CellReady(Me, objReadyArgs)     End If End Sub 
end example
 

The event fired in the provider part is implemented by the consumer part. Therefore, when the data is sent by the provider, the consumer part receives it and takes action. Listing 7-7 shows how a consumer might implement the CellReady event and use the passed data value to create a set of records from a database.

Listing 7-7: Receiving the Data
start example
 public void CellReady(object sender, CellReadyEventArgs cellReadyArgs) {     string strConn = "Password=" + password + ";Persist Security Info=True; User ID=" + userName + ";Initial Catalog=" + database + "; Data Source=" + sqlServer;     //Build SQL statement     string strSQL = "exec CustOrdersOrders '" + cellReadyArgs.Cell + "'";     DataSet dataSet = new DataSet("orders");     //Run the query     try     {         SqlConnection conn = new SqlConnection(strConn);         SqlDataAdapter adapter = new SqlDataAdapter(strSQL,conn);         adapter.Fill(dataSet,"orders");     }     catch(Exception x)     {         lblMessage.Text += x.Message + "<br>";     }     //Bind to grid     try     {         grdOrders.DataSource=dataSet;         grdOrders.DataMember="orders";         grdOrders.DataBind();     }     catch(Exception ex)     {         lblMessage.Text += ex.Message + "<br>";     } } 
end example
 

After the data is transferred, both web parts will draw their outputs through the RenderWebPart method. Whether or not the web part is involved in a connection does not make a difference as to how the output is rendered. In fact, you should remember that all of the methods that constitute the basic web part life cycle do not change. Therefore, everything you learned in Chapter 5 regarding initializing, loading, child controls, and rendering applies. When you design your web parts, you must combine the basic life cycle with the connection life cycle to achieve the behavior you want.

Using Transformers

Earlier in the chapter, I presented rules for interface compatibility. In that discussion, I said that certain interface pairs could be made compatible through the use of transformers. Transformers come into play in cases where a connected web part provides or consumes one of several different fields. In these scenarios, the end user must make a choice that maps the fields from the connected web parts. SPS always presents a visual tool for mapping fields when connected web parts require a transformer.

In order to provide the information necessary to map the fields, connected web parts that require a transformer must override the GetInitEventArgs method. In this method, a connected web part can tell the web part infrastructure what fields it supplies or consumes that are available for mapping. The web part infrastructure then uses this information to create the visual tool presented to the end user.

Each interface that requires a transformer supplies its field information through a class that inherits from InitEventArgs . Each event argument class accepts the appropriate metadata information necessary to describe the available fields ”usually in the form of an array of Strings . This information is then returned from the GetInitEventArgs method to the web part infrastructure. Listing 7-8 shows an example of a web part providing field information through IFilterConsumer .

Listing 7-8: Returning Field Data
start example
 Public Overrides Function GetInitEventArgs _ (ByVal strInterfaceName As String) As InitEventArgs     'Purpose: Provide a field list to pick from when connecting web parts.     'This will be the field that consumes the filter.     'Make sure we are being called on the IFilter interface     If strInterfaceName = "FilterConsumer" Then         'Create an object to hold the field list         Dim objFilterConsumerInitEventArgs As New FilterConsumerInitEventArgs         'The field list is created as an array of Strings         Dim strFieldNames(2) As String         Dim strFieldTitles(2) As String         strFieldNames(0) = "comp"         strFieldNames(1) = "cust"         strFieldNames(2) = "ord"         strFieldTitles(0) = "Company"         strFieldTitles(1) = "Customer"         strFieldTitles(2) = "Order"         'Put the data in the event argument         objFilterConsumerInitEventArgs.FieldList = strFieldNames         objFilterConsumerInitEventArgs.FieldDisplayList = strFieldTitles         'Pass the object back         Return objFilterConsumerInitEventArgs     Else         Return Nothing     End If End Function 
end example
 



Microsoft SharePoint[c] Building Office 2003 Solutions
Microsoft SharePoint[c] Building Office 2003 Solutions
ISBN: 1590593383
EAN: N/A
Year: 2006
Pages: 92

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net