Getting Started

Communicate Between ASP.NET Controls

You can make yourself more productive and simplify your application logic by creating Web Parts that pass data between themselves.

User-customizable Web pages get all the developer attention when discussing Web Parts. But there is another Web Parts feature that is just as important to ASP.NET developers: communication. Web Parts can talk to each other at both design time and run time, and you can take advantage of this to create richer, more robust applications.

For example, you can create a provider Web Part that lists identifiers, such as lists of employee, customer, or vendor names. You can then take advantage of this provider by creating a consumer Web Part that displays either specific information about those employees, customers, and vendors—such as addresses, contact names, and phone numbers—or all available information. Finally, you can write a dozen lines of code that enable you to connect these Web Parts together, with the Web Part displaying the information for the identifier that the user selects in the listing Web Part.

Creating these two Web Parts isn't difficult. However, you need to keep in mind certain issues when implementing Web Parts that can exchange information with one another. The first Web Part I'll show you lists employees (EmpList). The second Web Part displays employee detail information (EmpDisplay). Placing both these Web Parts on a page enables a user to select an employee from the list with the provider Web Part, while the second Web Part updates the display information about the selected employee automatically.

A Web Part must implement an interface you can pass data through before it can provide information. Creating a Web Part provider requires four steps. First, define an interface that specifies what data you want to pass between the Web Parts. Second, create a Web Part that implements the interface. Third, write the routines in the provider that supply the data. Finally, write the routine that identifies the Web Part as a provider.

Begin creating a Web Part provider by adding an interface module to a Visual Studio 2005 project. The simplest possible interface is a single property that returns a single string. This Visual Basic .NET code defines an interface called IEmpInfo:

Public Interface IEmpInfo
   Property ID() As String
End Interface

The same code in C# looks like this:

interface IEmpInfo: 
{
   string ID
      {
         get;
         set;
   }
}

Next, add a class module to your project that inherits from the WebPart class, then modify the class to provide data through the interface. In Visual Basic .NET, you implement an interface in a class simply by adding the Implements keyword with the name of the interface after the Inherits statement:

Public Class ProviderPart
   Inherits _
      System.Web.UI.WebControls.WebParts.WebPart
   Implements IEmpInfo

In C#, you add the interface name to the end of the class definition, preceded by a comma:

public class ProviderPart : 
   System.Web.UI.WebControls.WebParts.WebPart, 
   IEmpInfo

All that remains is to write the code for the properties or methods specified in the interface.

The VB.NET example includes a routine called EmpId that is tied to the ID property in the IEmpInfo interface through the Implements keyword. As specified in the interface, the EmpId routine is a property that returns a string and accepts no parameters:

Property ID() As String _
   Implements IEmpInfo.ID
   Get
      Return Me.lstEmpIds.SelectedItem
   End Get

   Set (Value As String)
      Me.lstEmpIds.SelectedItem = Value
   End Set

End Property

The equivalent C# code looks like this:

string IEmpInfo.ID
{
   get
   {
      return this.lstEmpIds.SelectedItem; 
   }
   set
   {
      this.lstEmpIds.SelectedItem = value; 
   }
}

The final step is to write the routine that identifies this Web Part as a provider. This routine is a function that returns a reference to the provider Web Part. You indicate that the routine is a connection point by adding the ConnectionProvider attribute to the routine. You must pass the ConnectionProvider a display name (this name is displayed if you let the user make the connection). This Visual Basic .NET routine creates the connection point for the IEmpInfo interface:

<WebControls.WebParts.ConnectionProvider( _
   "Provides ID")> Public Function _
   IEmpInfoProvider() As IEmpInfo
   Return Me
End Function

The C# version is just as simple to implement:

[WebControls.WebParts.ConnectionProvider(
   "Provides ID")]
public IEmpInfo IEmpInfoProvider()
{
   return this;
}

So far, you've created the code that retrieves the data and created a Web Part that can provide data to another part. The next step is to create the consumer Web Part.

Create a Consumer Part
Creating a consumer part is considerably easier than creating a provider. Begin by adding another class module to your project. Make it inherit from the WebPart class, create a connection point routine, and then read the methods or properties defined in the interface.

The connection point routine is a subroutine that accepts a single parameter declared as the interface type (IEmpInfo, for example). You can identify this routine as the connection point for the consumer by giving the subroutine the ConnectionConsumer attribute (again, passed a display name that would be used during user customization). When ASP.NET connects the consumer to the provider at run time, the Web Part consumer is passed a parameter that the consumer can use to access the property on the provider.

This Visual Basic .NET code goes one step further by defining a module-level variable (called iemp) that other routines in the consumer can access outside of the connection routine. You set iemp to the connection parameter in the connection routine, then use iemp in the control's Render routine to access the provider through the ID property defined in the interface. You need to make sure the Render routine doesn't attempt to read the provider's ID property before the connection is made, so the code in the Render method checks to see that iemp is not Nothing:

Private iemp As IEmpInfo

<WebControls.WebParts.ConnectionConsumer( _
   "IEmpInfo Consumer")> _
   Public Sub IEmpInfoConsumer( _
   ByVal iempPass As IEmpInfo)
      iemp = iempPass
   End Sub

Protected Overrides Sub Render( _
   ByVal writer As System.Web.UI.HtmlTextWriter)

   If iemp Is Nothing Then
      writer.Write( _
         "<b>No Employee " & _
         "information available.</b>")
   Else
      writer.Write("ID: <b>" & iemp.ID & "</b>")
   End If

End Sub

The equivalent C# code tests for null rather than Nothing:

private IEmpInfo iemp;

[WebControls.WebParts.ConnectionConsumer(
   "IEmpInfo Consumer")]
   public void IEmpInfoConsumer(IEmpInfo iempPass)
   {
      iemp = iempPass;
   }

protected override void 
   Render(System.Web.UI.HtmlTextWriter writer)
   {
      if(iemp == null)
      {
      writer.Write(
      "<b>No Employee information available.</b>");
      }
         else
      {
      writer.Write("ID: <b>" + iemp.ID + "</b>");
      }
   }

Classifying Web Parts as consumers and providers suggests that Web Parts support only a one-way flow of information. In fact, communication between Web Parts is more of a two-way street. A more complex interface might include both methods and properties. It looks like this in VB.NET:

Public Interface IEmpInfo
   Property ID() As String
   Function SortData(Direction As String) As Boolean
End Interface

And it looks like this in C#:

interface IEmpInfo
{
   string ID
   {
      get;
      set;
   }
   void UpdateCache(bool CacheFlag);
      bool SortData(string Direction);
}

A provider that implements this interface must also implement all the provider's routines:

Property ID() As String _
   Implements IEmpInfo.ID
      ?business logic
End Property

Function SortData(Direction As String) _
   As SuccessFlag
      ?business logic
End Sub

The C# equivalent requires a similar approach:

public string IEmpInfo.ID()
{
   ?business logic
}

public bool SortData(string Direction)
{
   ?business logic
}

The consumer that connects to this provider can call the methods, pass parameters, catch return values from functions, and both set and read properties. For example, this VB.NET code lets you take advantage of these kinds of abilities:

<WebControls.WebParts.ConnectionConsumer( _
   "IEmpInfo Consumer")> _
   Public Sub IEmpInfoConsumer( _
   ByVal iempPass As IEmpInfo)
   Dim bolSuccess As Boolean
   Dim strId As String

   strId = iempPass.ID
   iempPass.ID = "10345329"
   bolSuccess = SortData("Desc")

   End Sub

This C# code does the same:

[WebControls.WebParts.ConnectionConsumer(
   "IEmpInfo Consumer")]
public void IEmpInfoConsumer(IEmpInfo iempPass)
{
   bool bolSuccess;
      string strID;

   strId = iempPass.ID;
   iempPass.ID = "10345329"
   bolSuccess = SortData("Desc");
}

Make a Connection
You're now ready to test your Web Parts. The first step is to create a page where you can connect Web Parts. Begin by dragging a WebPartManager control onto a Web Form. You also need to add at least one WebPartZone and compile your solution, so the Web Parts appear in the Visual Studio toolbox.

You have everything in place at this point, so drag your consumer and provider into the WebPartZone. The next step is to add the code to connect your two Web Parts.

First, you need to retrieve the two Web Parts that you want to connect. This VB.NET code retrieves references to Web Parts in a zone called EmpSearch using the zone's WebParts collection. As the code shows, you must cast the returned value into the appropriate type because the WebParts collection returns a generic WebPart object:

Dim prov As MyWebParts.EmpList
Dim cons As MyWebParts.EmpDisplay

prov = CType( _
   Me.WebPartZone1.WebParts("EmpList1"), _
   MyWebParts.EmpList)
cons = CType _
   (Me.WebPartZone1.WebParts("EmpDisplay1"), _
   MyWebParts.DisplayEmp)

This code implements the same functionality in C#:

MyWebParts.EmpList prov;
MyWebParts.EmpDisplay cons;

prov = (MyWebParts.EmpList)
   this.WebPartZone1.WebParts("EmpList1");
      cons = (MyWebPartsVB.EmpDisplay) 
   this.WebPartZone1.WebParts("EmpDisplay1");

Next, you need to create references to the connection points in the Web Parts. You can retrieve a collection of all of the connection points of one type (either provider or consumer) for a Web Part by using the GetProviderConnectionPoints and GetConsumerConnectionPoints methods of the WebPartManager on the page (see Figure 1). For instance, the GetProviderConnectionPoints method returns a collection of all the provider connection points in the Web Part passed to the method. This VB.NET code retrieves the connection points for the two Web Parts retrieved previously:

Dim cncp As ConsumerConnectionPointCollection
Dim prcp As ProviderConnectionPointCollection

prcp = Me.WebPartManager1. _
   GetProviderConnectionPoints(prov)
cncp = Me.WebPartManager1. _
   GetConsumerConnectionPoints(cons)

This C# code does the same:

ConsumerConnectionPointCollection cncp;
ProviderConnectionPointCollection prcp;

prcp = this.WebPartManager1.
   GetProviderConnectionPoints(prov);
cncp = this.WebPartManager1.
   GetConsumerConnectionPoints(cons);

You're now ready to connect your Web Parts. You need to use a pair of related methods to implement this functionality: CanConnectWebParts and ConnectWebParts. The CanConnectWebParts method tests to see whether you can connect two Web Parts, returning True if you can connect them. Note that you cannot connect Web Parts that don't share an interface, unless you write special code to handle data conversions. The ConnectWebParts method makes the connection between the parts.

Both methods accept the same parameters: the provider, the connection point in the provider, the consumer, and the connection point in the consumer. For the connection point parameters, you can use a member of connection point collections, which you retrieve from the Get*ConnectionPoints methods. The collections represent all the connection points in the Web Part, so you must specify which of the connection points you want to use. Fortunately, the Web Parts described in this article contain only a single connection. This VB.NET code connects the Web Parts you retrieved:

If Me.WebPartManager1. _
   CanConnectWebParts(prov, prcp(0), cons, cncp(0)) _
   Then
   Me.WebPartManager1. _
      ConnectWebParts(prov, prcp(0), cons, cncp(0))
End If

This code uses the first connection point in each of their connection point collections, but only after making sure that you can connect the Web Parts. You take a similar approach in C#:

if(this.WebPartManager1.
   CanConnectWebParts(prov, prcp(0), cons, cncp(0)))
{
   this.WebPartManager1.
      ConnectWebParts(prov, prcp(0), cons, cncp(0));
}

You can also break connections through the Connections collection on the WebPartManager by using the Disconnect method. This VB.NET code retrieves the first connection on the page and breaks the connection:

Dim cn As WebParts.Connection
cn = Me.WebPartManager1.Connections(0)
Me.WebPartManager1.DisconnectWebParts(cn)

This C# code also retrieves and breaks the first connection:

WebParts.Connection cn;
cn = this.WebPartManager1.Connections(0);
this.WebPartManager1.DisconnectWebParts(cn);

The example in this article only hints at what you can accomplish when you start connecting Web Parts. If you want the connection to be always present, it doesn't make much sense to make the connection at run time every time you request a page. Fortunately, you can make a permanent connection between Web Parts by using tags in your ASPX page. Alternatively, you can let your users make the connections that they want at run time. All you need to do is add a ConnectionsZone to your page and set the WebPartManager's DisplayMode property. The .NET Framework helps you ensure that your Web Parts can communicate with other Web Parts by including three predefined interfaces you can implement in your Web Parts: IField, IRow, and ITable. It's even possible to connect two Web Parts with different interfaces by building a transformer.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube