We've used the example of a weather WebPart several times in this chapter, and in early examples this used a ZipCode property to state the location to display the weather for. A better solution would be to have the Zip Code supplied to the WebPart, perhaps by another WebPart. This is achieved by WebPart connections, where data can be supplied directly from one part to another. WebPart connection revolves around providers that supply data and consumers that use data. Both providers and consumers use a common interface to define the data being passed between them, and the WebPartManager manages the connection. Figure 13.15 shows the flow of data between the provider and consumer, and how the WebPartManager controls this. Figure 13.15. WebPart Connections data flow
The steps are:
Implementing WebPart ConnectionsImplementing connections requires an interface to define the data and for a Provider to implement that interface. The Consumer doesn't need to implement the interface but will require a reference to it, because it is the interface that is used to pass the data. Creating the InterfaceThe first part of implementing WebPart connections is to create an interface that defines the data. For example, the weather WebPart requires a Zip Code, the interface for which is defined in Listing 13.15. Listing 13.15. The Zip Code Interface
This simply defines a read-only property, ZipCode, which is the only piece of data needed. The interface can be more complex, containing properties and methods, providing a great deal of flexibility when connecting WebParts. Implementing the Interface in the ProviderWithin the provider, the interface is implemented to supply the data to consumers. In our example, the Contacts WebPart is a Provider and is also a user control. The code-behind for this user control is shown in Listing 13.16, where the IZipCode interface is implemented. There are some important points to notice about this code:
Listing 13.16. Creating the Provider
Using the Interface in the ConsumerWithin the Consumer, the interface isn't implemented. Instead, a connection end point is defined that accepts an instance of the interface, as shown in Listing 13.17. Listing 13.17. Consuming the Connection Interface
Here there is a method that takes a single parameter of type IZipCodethe interface. The WebPartManager receives the interface from the Provider and passes it to the Consumer using this method. In the example code, the interface is simply stored in a variable for later use (which we'll come to in a moment). The Consumer method is also decorated with an attribute, in this case the ConnectionConsumer attribute, the parameters of which are the description, the connection point name, and optionally, whether multiple connections can be made to this end point. Listing 13.18 shows how the Consumer uses the interface from the Provider. First it is checked for a null value, which would indicate there is no connection in place. If the connection is in place, the property defined by the interface is accessed to fetch the data from the Provider; in this example it is used to format a query to the Yahoo weather RSS feed, which supplies a forecast for several days. Listing 13.18. Using the Data from the Provider
Once the interface and end points have been defined, you need to connect the WebParts together. Connections between WebParts can be static or dynamic. Static connections are defined by the page creator, while dynamic connections are created by the user at runtime. Static connections are created within the StaticConnections section of the WebPartManager, as shown in Listing 13.19. The connection details are defined in four properties of the WebPartConnection:
Listing 13.19. Creating Static Connections
Because this connection is permanent, data will be supplied from the Provider to the Consumer as long as both parts are on the page. For example, consider Figure 13.16, which shows the Contacts and Weather WebParts, which are connected, even though it doesn't appear to be so. The reason is that the data provided is for the DetailsView, and no contact is currently selected. Figure 13.16. Connected WebParts (with no data selected)
If a contact is selected, data is available to be provided to the Consumer, so the weather details automatically show. User-Initiated ConnectionsYou can allow users to connect parts together or to disconnect WebParts (even statically created ones) by using a ConnectionsZone, as shown in Listing 13.20. Listing 13.20. Declaring a ConnectionsZone
When the DisplayMode of the page is set to ConnectDisplayMode, the Connect verb is added to the verbs for WebParts. Selecting this displays the ConnectionsZone, the contents of which depend upon the current connection state of the WebPart (see Figure 13.17). For a WebPart with no current connections, you would see a message indicating that no connections are present, with a link to create a connection. For example, Figure 13.18 shows the connection zone for a Provider that has no active connections. Figure 13.17. Connected WebParts (with data selected)Figure 13.18. The Connections Zone for a Provider with no active connections
For a WebPart with connections, you see the current connections (with an option to delete them), as well as the link to create connections, as shown in Figure 13.19. This shows the Contacts WebPart with a connection to the My Weather WebPart, sending the Zip Code (the description for the data being sent is taken from the ConnectionProvider attribute). For providers, the interface has the same layout but with the appropriate name changes (see Figure 13.20). Figure 13.19. The Connections Zone for a Provider with active connections
Figure 13.20. Connections Zone for a Consumer with active connections
Whether on a Provider or Consumer, clicking Disconnect will remove the connection between the WebParts. Clicking the Connect link allows connection to a WebPart. For example, Figure 13.21 shows connecting from the Consumergetting data from the Contacts WebPart, and Figure 13.22 shows the opposite, connecting from a Providersending data to the Weather WebPart. Figure 13.21. Connecting to a Provider
Figure 13.22. Connecting to a Consumer
WebParts can be both Providers and Consumers, consuming or supplying data from a number of WebParts. Connecting to WebParts in Master PagesWhen using Master Pages, you will generally have the WebPartManager on the Master Page. If you wish to define connections between WebParts on the Master Page and the content page, then you need to use a ProxyWebPartManager on the content page, as shown in Listing 13.21. Listing 13.21. Declaring a ProxyWebPartManager
The ProxyWebPartManager, as its name suggests, is simply a proxy to the WebPartManager on the Master Page. TransformersWhen connecting WebParts, you obviously aim for flexibility, and perhaps you want a WebPart to either provide or consume a variety of different data types. If writing your own WebParts, this is easy to achieve since you can add the types to the interface, but if using third-party WebParts, you may have to deal with data in formats you aren't expecting. The solution to this is to provide transformers, which transform data from one type to another. A transformer is a class that converts data between types exposed by interfacesthe interfaces that Consumers and Providers use. For example, consider two WebParts that expose data via interfaces, one of which exposes integer data while the other exposes string data. By default, you would not be able to connect these, because the data types are incompatible. However, by creating a transformer, you can transform the data as it flows between the WebParts. To make this easy to understand, consider two WebParts, one that provides string data, and one that consumes integer data. While it is easy to convert between these two, the principles of a transformer are easier to understand with simple types, and the technique can be applied between any types. For example, consider Listing 13.22, which implements a WebPartTrasnformer, converting data from a string provider to an integer consumer. The transformer is identified by the WebPartTransformer attribute, which defines the type to convert from and the type to convert to. The class inherits from WebPartTransformer and implements the IIntegerData interfacethe interface used by the consumer. The TRansform method provides access to the string data, and the implementation if the IIntegerData interface simply converts the data from this interface. Listing 13.22. A Sample Transformer
Before a transformer can be used, it must be defined in the webParts transformers section of web.config. The type should be set to the full type name of the transformer. Listing 13.23. Configuring the Transformer
At runtime, the transformer is injected into the connection between WebParts, first by allowing the connection to take place (because the transformer converts between incompatible types) and then by providing the actual conversion process. The great point about this is that the technique can be used for converting data from third party WebParts without having to get the third party to expose (or consume) data in a form it doesn't require. You can write the transformer yourself, integrating previously incompatible WebParts. |