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.

Comments (11)

  1. Goran Tesic says:

    Hi Jerbear,

    Your article is very useful, but I’d like to see the code for web part that behaves as provider and consumer at the same time.

    If it’s not possible to do such a thing, please, point us to right direction and tips related to this thing.

    Thank you in advace.

    Goran

  2. Jerry Dixon says:

    Hi Goran,

    I don’t have such an example handy, but it wouldn’t be that hard to create.  You use the same principles that are demonstrated here.  The big gotcha would be if the web part needed to accept data from one web part before it could send it to another.  You’d have to be very careful how you set that up.  Otherwise, everything should be the same.

    I’ve been swamped at work recently; I’ll try to work up an example of this once I catch back up.

    Thanks,

    Jerry

  3. Goran Tesic says:

    Hi Jerry,

    I was succeed to make web part to be provider and consumer at the same time and I’d like to know what could be the biggest problem when you try to use that web part in real situation. I have such a sample. I’d like to use web part that acts as provider and consumer at the same on SharePoint server (MOSS2007). For example, I have 3 web parts: p1, p2 and p3. p1 should be provider for p2 and p3. When I connect p2 to p1, data are shown in p2, which is ok. But, when I try to connect p3 to p1 as well, data will be shown in p3, but not in p2, in spite of the fact that connection still exists. Actually, all connections, p2 to p1 and p3 to p1 exist, but data are displayed in p1 (provider) and in p3 (consumer), but not in p2 (consumer). This means only last web part in the chain displays data. Do you have any suggestion in this case?

    Thank you in advance.

    Goran

  4. Jerry Dixon says:

    Hi Goran,

    I think you may have explained your situation incorrectly.  You mentioned that P2 and P3 both connect to P1.  I demonstrate how to do that in Part 2 of my series.  (HanoiDisks provides the number of disks selected to HanoiCount and HanoiSteps.)    However, I believe that you meant to describe this:  P1->P2->P3.  Am I correct?

  5. In part 1 of this series, I showed how to make one web part accept data from another. In part 2 , I showed

  6. In part 1 of this series, I showed how to make one web part accept data from another. In part 2 , I showed

  7. Goran Tesic says:

    Hi again Jerry,

    Actually, I’m able to make multlple web parts of the same type (they are providers and consumers at the same time) to work in a “chain mode” (p1->p2->p3), but I’m not able to make them to work in “parallel mode” (p1->p2, p1->p3). It turns that only last web part can display data. For example, if I connect p2 to p1 first (p1->p2), it works fine. But, when I connect p3 to p1 (p1->p3) after that, the data are displayed only in p3 and p2 is blank. But, all connections are still there.

    Is there anything that I can do with connetion points?

    Thank you for all Jerry.

    Goran

  8. Jerry Dixon says:

    Hi Goran,

    I don’t understand why you are seeing that behavior.  If you look at my examples, you’ll see that I’m sending data from HanoiDisks to HanoiCount, and from HanoiDisks to HanoiSteps.  What is the difference between my samples and your code?

    Jerry

  9. Goran Tesic says:

    Hi Jerry,

    Actually, I’m working on very complex web part that another guy created before me and I must use TableCallback object on class level. In GetTableData method I’m only assigning TableCallback parameter to TableCallback object, which is on class level. It looks as follows:

    public void GetTableData(TableCallback callback)

    {

     tableCallback = callback;

    }

    After that, I’m calling this:

    this.tableCallback(this.providerTable.Rows);

    in OnPreRender method.

    Do you happen to know is there any other way to do this?

    Thanks in advance.

    Goran Tesic

Skip to main content