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
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 SystemImports System.DrawingImports System.ComponentModelImports System.Web.UI.HtmlControlsImports System.Web.UI.WebControlsImports 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 PropertyEnd Class
HanoiColor.vb
Imports SystemImports System.DrawingImports System.ComponentModelImports System.Web.UI.HtmlControlsImports System.Web.UI.WebControlsImports 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 PropertyEnd 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 SystemImports System.DrawingImports System.ComponentModelImports System.Web.UI.HtmlControlsImports System.Web.UI.WebControlsImports 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
HanoiCount.vb
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
Try _headerMessage = New Literal Me.Controls.Add(_headerMessage) Catch ex As Exception Me.Controls.Clear()
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
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)
<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:
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.