Josh Holmes recently asked me to present a couple of small demos for an ArcReady event in Memphis. While looking through my material, I discovered a couple of web parts that I had written a while ago. I wrote these web parts in order to investigate how to send data from one part to another. They illustrate how easy it is to set up Web Part Connections in WSS 3.0, so I thought I'd share them here.
In this post, I'll show how to send data from one web part to another. Later posts will demonstrate how one web part can provide data to multiple web parts, how one web part can consume data from multiple parts, and how a web part can be a provider and a consumer at the same time. As you will see, with WSS 3.0, this is very easy to do.
Now, programs need a purpose, and web parts are no different. These web parts provide different types of information about the Towers of Hanoi puzzle. In this post, I'll use two web parts. One web part contains a drop-down list that can be used to specify the number of disks used for the puzzle. The second web part contains a list box that enumerates the steps needed to solve the puzzle with the number of disks specified in the first web part. For this to work properly, the second web part must obtain data from the first web part. So, how does this work?
First, data is exchanged via interfaces. In this case, the interface is called IDisks:
IDisks.vb Public Interface IDisks ReadOnly Property NumberOfDisks() As Integer End Interface
IDisks.vb
Public Interface IDisks
ReadOnly Property NumberOfDisks() As Integer
End Interface
This interface will be used to pass the selected number of disks from one web part to the other.
The first web part will allow the user to select the number of disks that should be used for the puzzle. It is called HanoiDisks:
HanoiDisks.vb Imports SystemImports System.ComponentModelImports System.Web.UI.HtmlControlsImports System.Web.UI.WebControlsImports System.Web.UI.WebControls.WebParts Public Class HanoiDisks Inherits WebPart Implements IDisks Protected diskList As DropDownList = Nothing Protected Overrides Sub CreateChildControls() MyBase.CreateChildControls() Dim t As Table = Nothing Dim tr As TableRow = Nothing Dim td As TableCell = Nothing Try diskList = New DropDownList With diskList .AutoPostBack = True .Items.Clear() .Items.Add(New ListItem("Three", 3)) .Items.Add(New ListItem("Four", 4)) .Items.Add(New ListItem("Five", 5)) .Items.Add(New ListItem("Six", 6)) .Items.Add(New ListItem("Seven", 7)) .Items.Add(New ListItem("Eight", 8)) .SelectedIndex = 0 End With t = New Table tr = New TableRow td = New TableCell td.Text = "Number of Disks: " tr.Controls.Add(td) td = New TableCell td.Controls.Add(diskList) tr.Controls.Add(td) t.Controls.Add(tr) Me.Controls.Add(t) 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("Number of Disks")> _ Public Function GetDiskInterface() As IDisks Return Me End Function Public ReadOnly Property NumberOfDisks() As Integer Implements IDisks.NumberOfDisks Get Return diskList.SelectedValue End Get End Property End Class
HanoiDisks.vb
Imports SystemImports System.ComponentModelImports System.Web.UI.HtmlControlsImports System.Web.UI.WebControlsImports System.Web.UI.WebControls.WebParts
Public Class HanoiDisks Inherits WebPart Implements IDisks
Protected diskList As DropDownList = Nothing
Protected Overrides Sub CreateChildControls() MyBase.CreateChildControls()
Dim t As Table = Nothing Dim tr As TableRow = Nothing Dim td As TableCell = Nothing
Try diskList = New DropDownList
With diskList .AutoPostBack = True
.Items.Clear()
.Items.Add(New ListItem("Three", 3)) .Items.Add(New ListItem("Four", 4)) .Items.Add(New ListItem("Five", 5)) .Items.Add(New ListItem("Six", 6)) .Items.Add(New ListItem("Seven", 7)) .Items.Add(New ListItem("Eight", 8))
.SelectedIndex = 0 End With
t = New Table tr = New TableRow
td = New TableCell td.Text = "Number of Disks: " tr.Controls.Add(td)
td = New TableCell td.Controls.Add(diskList) tr.Controls.Add(td)
t.Controls.Add(tr)
Me.Controls.Add(t) 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("Number of Disks")> _ Public Function GetDiskInterface() As IDisks Return Me End Function
Public ReadOnly Property NumberOfDisks() As Integer Implements IDisks.NumberOfDisks Get Return diskList.SelectedValue End Get End Property
End Class
Notice that HanoiDisks inherits from WebPart. This is the standard .NET 2.0 WebPart class, and it performs most of the work for us. All we have to do is override CreateChildControls to add our UI. The HanoiDisks class also implements the IDisks interface that was mentioned earlier. It returns the value of the selected item in the dropdown list in our UI.
The GetDiskInterface method is the key to the web part communication. It returns the HanoiDisks class as an instance of the IDisks interface. It uses the ConnectionProvider attribute to inform WSS that this method provides data for another web part. WSS does the rest.
Now let's look at the HanoiSteps web part, which lists the steps needed to solve the puzzle.
HanoiSteps.vb Imports SystemImports System.ComponentModelImports System.Web.UI.HtmlControlsImports System.Web.UI.WebControlsImports System.Web.UI.WebControls.WebParts Public Class HanoiSteps Inherits WebPart Protected _diskInterface As IDisks = Nothing Protected _disks As Integer = 0 Protected _stepList As ListBox = Nothing Protected _headerMessage As Literal = Nothing Protected Overrides Sub CreateChildControls() MyBase.CreateChildControls() Try _headerMessage = New Literal Me.Controls.Add(_headerMessage) Dim br As New HtmlGenericControl("br") Me.Controls.Add(br) _stepList = New ListBox With _stepList .SelectionMode = ListSelectionMode.Single .Rows = 7 End With Me.Controls.Add(_stepList) 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 End Sub Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter) If _headerMessage IsNot Nothing Then _headerMessage.Text = "Here are the steps for " & _disks & " disks:" End If If _stepList IsNot Nothing AndAlso _disks > 1 Then FillListWithSteps(_stepList, _disks) End If MyBase.RenderContents(writer) End Sub <ConnectionConsumer("Number of Disks")> _ Public Sub AcceptDiskInterface(ByVal diskInterface As IDisks) _diskInterface = diskInterface End Sub Private Sub FillListWithSteps(ByVal list As ListBox, ByVal disks As Integer) list.Items.Clear() Towers(list, disks, 1, 3, 2) End Sub Private Sub Towers(ByVal list As ListBox, ByVal disks As Integer, ByVal fromPole As Integer, ByVal toPole As Integer, ByVal usingPole As Integer) If disks = 1 Then list.Items.Add(String.Format("Move a disk from pole {0} to pole {1}", fromPole, toPole)) Else Towers(list, disks - 1, fromPole, usingPole, toPole) Towers(list, 1, fromPole, toPole, usingPole) Towers(list, disks - 1, usingPole, toPole, fromPole) End If End Sub End Class
HanoiSteps.vb
Public Class HanoiSteps Inherits WebPart
Protected _diskInterface As IDisks = Nothing Protected _disks As Integer = 0 Protected _stepList As ListBox = Nothing Protected _headerMessage As Literal = Nothing
Try _headerMessage = New Literal Me.Controls.Add(_headerMessage)
Dim br As New HtmlGenericControl("br") Me.Controls.Add(br)
_stepList = New ListBox
With _stepList .SelectionMode = ListSelectionMode.Single .Rows = 7 End With
Me.Controls.Add(_stepList) 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
Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
If _headerMessage IsNot Nothing Then _headerMessage.Text = "Here are the steps for " & _disks & " disks:" End If
If _stepList IsNot Nothing AndAlso _disks > 1 Then FillListWithSteps(_stepList, _disks) End If
MyBase.RenderContents(writer)
<ConnectionConsumer("Number of Disks")> _ Public Sub AcceptDiskInterface(ByVal diskInterface As IDisks) _diskInterface = diskInterface End Sub
Private Sub FillListWithSteps(ByVal list As ListBox, ByVal disks As Integer)
list.Items.Clear()
Towers(list, disks, 1, 3, 2)
Private Sub Towers(ByVal list As ListBox, ByVal disks As Integer, ByVal fromPole As Integer, ByVal toPole As Integer, ByVal usingPole As Integer)
If disks = 1 Then list.Items.Add(String.Format("Move a disk from pole {0} to pole {1}", fromPole, toPole)) Else Towers(list, disks - 1, fromPole, usingPole, toPole) Towers(list, 1, fromPole, toPole, usingPole) Towers(list, disks - 1, usingPole, toPole, fromPole) End If
Most of this code is needed just to make this web part work. That is, to create UI controls that list the steps needed to solve the puzzle. The AcceptDiskInterface method is the key to accepting data from the first web part. It accepts an instance of the IDisks interface and stores it for later use. The ConnectionConsumer attribute is used to tell WSS that this method will consume data. The OnPreRender method checks to see if the interface has been received. If so, it stores the number of disks in a variable. The RenderContents method uses this variable to determine how many disks have been specified, and what steps are needed to solve the puzzle.
All that's left is to edit one of the web parts, select the connections menu, and link the parts together.
This completes the first post. We've seen how one web part can pass data to another. Look for another post soon, where I'll expand on this topic.
Note that, while you are free to use this code as you wish, it comes with no guarantees. I wrote it simply for illustration purposes, not for actual use.