Web Part Connections in WSS 3.0 (Part 3)

In part 1 of this series, I showed how to make one web part accept data from another.  In part 2, I showed that this can be extended so that one web part can provide data for many.  Now, in this final post of the series, I'll show how a single web part can accept data from multiple web parts.  As you will see, we need to make only a few changes for this to happen.

Let's continue from where we left off.  Our page now has three web parts.  The first, HanoiDisks, allows the user to select the number of disks that should be used for our Towers of Hanoi puzzle.  The second web part, HanoiSteps, displays the steps needed to solve the puzzle.  The third web part, HanoiCount, displays the number of steps that will be needed to complete the puzzle.  Now we'll add a fourth web part.  This web part, HanoiColor, will allow the user to select the font color used by the HanoiCount web part.  HanoiCount will then be using data from both the the HanoiColor and the HanoiDisk web parts.

So far, we've been passing around instances of the IDisks interface.  We're now going to pass color information, so we need a new interface:

IColor.cb 

Public Interface IColor

    ReadOnly Property Color() As Integer

End Interface

This interface will allow a color number to be passed from HanoiColor to HanoiCount.  First, lets look at the new HanoiColor web part:

HanoiColor.vb 

Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiColor
Inherits WebPart
Implements IColor

    Protected _colorList As DropDownList = Nothing

    Protected Overrides Sub CreateChildControls()
MyBase.CreateChildControls()

        Try
_colorList = New DropDownList

            With _colorList
.AutoPostBack = True

                .Items.Clear()

                .Items.Add(New ListItem("Black", 0))
.Items.Add(New ListItem("Blue", 1))
.Items.Add(New ListItem("Green", 2))
.Items.Add(New ListItem("Red", 3))

                .SelectedIndex = 0
End With

            Dim lit As New Literal()
lit.Text = "Text Color: "
Me.Controls.Add(lit)

            Me.Controls.Add(_colorList)
Catch ex As Exception
Me.Controls.Clear()

            Dim msg As New Literal()
msg.Text = ex.Message
Me.Controls.Add(msg)
End Try

    End Sub

    <ConnectionProvider("Text Color")> _
Public Function GetColorInterface() As IColor
Return Me
End Function

    Public ReadOnly Property Color() As Integer Implements IColor.Color
Get
Return _colorList.SelectedValue
End Get
End Property
End Class

By now, you should recognize all of the features of this code.  The GetColorInterface method provides an instance of the IColor interface, and uses the ConnectionProvider attribute to communicate this fact to WSS.

Now, let's look at the HanoiCount web part.  It now needs to accept data from two different sources, so some code changes are needed:

HanoiCount.vb

Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiCount
Inherits WebPart

    Protected _diskInterface As IDisks = Nothing
Protected _disks As Integer = 0

    Protected _colorInterface As IColor = Nothing
Protected _color As String = "#000000;"

    Protected _headerMessage As Literal = Nothing

    Protected Overrides Sub CreateChildControls()
MyBase.CreateChildControls()

        Try
_headerMessage = New Literal
Me.Controls.Add(_headerMessage)
Catch ex As Exception
Me.Controls.Clear()

            Dim msg As New Literal()
msg.Text = ex.Message
Me.Controls.Add(msg)
End Try

    End Sub

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
MyBase.OnPreRender(e)

        If _diskInterface IsNot Nothing Then
_disks = _diskInterface.NumberOfDisks
End If

        If _colorInterface IsNot Nothing Then
_color = GetColorFromNumber(_colorInterface.Color)
End If

    End Sub

    Private Function GetColorFromNumber(ByVal colorNumber As Integer) As String

        Dim selectedColor As String = "#000000;"

        Select Case colorNumber
Case 0
selectedColor = "#000000;"
Case 1
selectedColor = "#0000FF;"
Case 2
selectedColor = "#00FF00;"
Case 3
selectedColor = "#FF0000;"
End Select

        Return selectedColor

    End Function

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

        If _headerMessage IsNot Nothing AndAlso _disks > 1 Then
Dim steps As Integer = 2 ^ _disks - 1
With _headerMessage
.Text = "<span style=""color:" & _color & """>"
.Text &= "There are " & steps & " steps for " & _disks & " disks."
.Text &= "</span>"
End With
End If

        MyBase.RenderContents(writer)

    End Sub

    <ConnectionConsumer("Number of Disks", "1")> _
Public Sub AcceptDiskInterface(ByVal diskInterface As IDisks)
_diskInterface = diskInterface
End Sub

    <ConnectionConsumer("Text Color", "2")> _
Public Sub AcceptColorInterface(ByVal colorInterface As IColor)
_colorInterface = colorInterface
End Sub

End Class

Notice that two interfaces are used here.  This fact is reflected throughout most of the code.  Also notice that there are two methods that accept interfaces; AcceptDiskInterface and AcceptColorInterface.  Look closely at the ConnectionConsumer attributes.  Up until now, I've supplied only one parameter to the attribute constructor.  This works just fine when there is only one ConnectionConsumer or ConnectionProvider attribute used by a web part.  When you have more than one, however, you need to also supply a unique identifier for each attribute.  This is the real trick to using multiple web part connections.

At this point, we've written four web parts that communicate with each other in various ways:

  • HanoiCount - provides an instance of the IColor interface to the HanoiCount web part
  • HanoiDisks - provides an instance of the IDisks interface to the HanoiCount and HanoiSteps web parts
  • HanoiCount - accepts an instance of the IColor interface from the HanoiColor web part, and an instance of the IDisks interface from the HanoiDisks web part
  • HanoiSteps - accepts an instance of the IDisks interface from the HanoiDisks web part

I leave it to the reader to create a final web part; one that is both a provider and a consumer of data.  The can be accomplished by using the same techniques presented in this series of posts.

I hope these posts have helped you get started with web part communication.  This can be a very useful feature of WSS 3.0.