Common Custom tasks
Common_CustomTasks_VBNet\FindNearTask_VBNet\TaskResultsPanel.vb
' Copyright 2010 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

Imports Microsoft.VisualBasic
Imports System
<Assembly: System.Web.UI.WebResource("TaskResultsPanel.js", "text/javascript")> 

Namespace FindNearTask_VBNet
    Public Class TaskResultsPanel
        Inherits ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanel
#Region "Instance Variables"

        Private _resultsGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer
        Private _map As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map

#End Region

#Region "Public Properties"

        ' Maximum panel width when the panel is first shown
        Public Property InitialMaxWidth() As System.Web.UI.WebControls.Unit
            Get
                If Me.StateManager.GetProperty("InitialMaxWidth") Is Nothing Then
                    Return New System.Web.UI.WebControls.Unit(-1, System.Web.UI.WebControls.UnitType.Pixel)
                Else
                    Return CType(Me.StateManager.GetProperty("InitialMaxWidth"), System.Web.UI.WebControls.Unit)
                End If
            End Get
            Set(ByVal value As System.Web.UI.WebControls.Unit)
                Me.StateManager.SetProperty("InitialMaxWidth", value)
            End Set
        End Property

        ' Maximum panel height when the panel is first shown
        Public Property InitialMaxHeight() As System.Web.UI.WebControls.Unit
            Get
                If Me.StateManager.GetProperty("InitialMaxHeight") Is Nothing Then
                    Return New System.Web.UI.WebControls.Unit(-1, System.Web.UI.WebControls.UnitType.Pixel)
                Else
                    Return CType(Me.StateManager.GetProperty("InitialMaxHeight"), System.Web.UI.WebControls.Unit)
                End If
            End Get
            Set(ByVal value As System.Web.UI.WebControls.Unit)
                Me.StateManager.SetProperty("InitialMaxHeight", value)
            End Set
        End Property

        ' Gets the Map control associated with the panel
        Public ReadOnly Property MapInstance() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map
            Get
                ' Return the map member variable if it already references a Map control
                If _map IsNot Nothing Then
                    Return _map
                End If

                ' If the MapID has been initialized, get the control referenced by that ID
                If (Not String.IsNullOrEmpty(Me.MapID)) Then
                    _map = TryCast(ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(Me.MapID, Me.Page), ESRI.ArcGIS.ADF.Web.UI.WebControls.Map)
                End If

                Return _map
            End Get
        End Property

        ' ID of the Map control associated with the panel
        Public Property MapID() As String
            Get
                Return TryCast(Me.StateManager.GetProperty("ResultsPanelMapID"), String)
            End Get
            Set(ByVal value As String)
                ' Write the ID to state
                Me.StateManager.SetProperty("ResultsPanelMapID", value)
                ' Reset the Map member variable so the map is retrieved next time the MapInstance
                ' property is referenced
                _map = Nothing
            End Set
        End Property

        ' Gets the GraphicsLayer associated with the panel
        Public ReadOnly Property GraphicsLayer() As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer
            Get
                If _resultsGraphicsLayer IsNot Nothing Then
                    Return _resultsGraphicsLayer
                End If

                _resultsGraphicsLayer = Me.GetGraphicsLayer()
                Return _resultsGraphicsLayer
            End Get
        End Property

#End Region

#Region "Private Properties"

        ' Name of the resource containing the associated GraphicsLayer
        Private Property ResourceName() As String
            Get
                Return TryCast(Me.StateManager.GetProperty("ResultsResourceName"), String)
            End Get
            Set(ByVal value As String)
                Me.StateManager.SetProperty("ResultsResourceName", value)
            End Set
        End Property

        ' Name of the GraphicsLayer
        Private Property LayerName() As String
            Get
                Return TryCast(Me.StateManager.GetProperty("ResultsLayerName"), String)
            End Get
            Set(ByVal value As String)
                Me.StateManager.SetProperty("ResultsLayerName", value)
            End Set
        End Property

        ' Stores the width of the panel's contents
        Private Property ContentWidth() As Integer
            Get
                Return If(StateManager.GetProperty("ContentWidth") Is Nothing, 0, CInt(Fix(StateManager.GetProperty("ContentWidth"))))
            End Get
            Set(ByVal value As Integer)
                StateManager.SetProperty("ContentWidth", value)
            End Set
        End Property

        ' Stores the height of the panel's contents
        Private Property ContentHeight() As Integer
            Get
                Return If(StateManager.GetProperty("ContentHeight") Is Nothing, 0, CInt(Fix(StateManager.GetProperty("ContentHeight"))))
            End Get
            Set(ByVal value As Integer)
                StateManager.SetProperty("ContentHeight", value)
            End Set
        End Property

#End Region

#Region "ASP.NET WebControl Life Cycle Event Handlers - CreateChildControls"

        ' Creates the panel interface
        Protected Overrides Sub CreateChildControls()
            Controls.Clear()
            MyBase.CreateChildControls()

            ' If the panel does not have an associated GraphicsLayer, exit the function
            If Me.GraphicsLayer Is Nothing Then
                Return
            End If

            ' Create a div to hold all the panel's content
            Dim contentDiv As New System.Web.UI.HtmlControls.HtmlGenericControl("div")
            contentDiv.ID = "ContentDiv"
            contentDiv.Style(System.Web.UI.HtmlTextWriterStyle.Overflow) = "auto"

            ' If the content height and width have already been calculated, apply them to the
            ' content div
            If Me.ContentWidth > 0 Then
                contentDiv.Style(System.Web.UI.HtmlTextWriterStyle.Width) = String.Format("{0}px", Me.ContentWidth.ToString())
            End If
            If Me.ContentHeight > 0 Then
                contentDiv.Style(System.Web.UI.HtmlTextWriterStyle.Height) = String.Format("{0}px", Me.ContentHeight.ToString())
            End If

            ' Add the div to the task's controls collection
            Me.Controls.Add(contentDiv)

            ' Create a table to store the task results and initialize its styling
            Dim resultsTable As New System.Web.UI.WebControls.Table()
            resultsTable.ID = "ContentTable"
            resultsTable.Style("border-right") = "silver 2px solid"
            resultsTable.Style("border-bottom") = "silver 2px solid"
            resultsTable.CellPadding = 0

            ' If the content height and width have already been calculated, apply them to the
            ' results table
            If Me.ContentWidth > 0 Then
                resultsTable.Style(System.Web.UI.HtmlTextWriterStyle.Width) = String.Format("{0}px", Me.ContentWidth.ToString())
            End If
            If Me.ContentHeight > 0 Then
                resultsTable.Style(System.Web.UI.HtmlTextWriterStyle.Height) = String.Format("{0}px", Me.ContentHeight.ToString())
            End If

            ' Add the table to the content div
            contentDiv.Controls.Add(resultsTable)

            ' Get the indexes of the columns to be displayed
            Dim displayColumnIndexList As System.Collections.Generic.List(Of Integer) = Me.GetDisplayColumnIndexes()

            Dim columnWidthList As New System.Collections.Generic.List(Of Single)()
            Dim rowHeightList As New System.Collections.Generic.List(Of Single)()

            ' Create a header row and add it to the table
            resultsTable.Rows.Add(Me.CreateHeaderRow(displayColumnIndexList, columnWidthList, rowHeightList))

            ' Create data rows and add them to the table
            resultsTable.Rows.AddRange(Me.CreateDataRows(displayColumnIndexList, columnWidthList, rowHeightList))

            ' Calculate the table width by summing the stored widths of the text of each column, then adding the 
            ' width of each column's border.
            Dim tableWidth As Single = 0
            For Each columnWidth As Single In columnWidthList
                tableWidth += columnWidth
            Next columnWidth
            tableWidth += columnWidthList.Count * 2
            tableWidth = CSng(System.Math.Round(tableWidth + 0.5))

            ' Calculate the table height by summing the stored heights of the text of each row, then adding the
            ' height of each column's border.
            Dim tableHeight As Single = 0
            For Each rowHeight As Single In rowHeightList
                tableHeight += rowHeight * 2
            Next rowHeight
            tableHeight += rowHeightList.Count
            tableHeight = CSng(System.Math.Round(tableHeight + 0.5))

            ' Apply the calculated dimensions to the task, the content div, and the table containing the results
            If tableWidth > 0 AndAlso tableHeight > 0 Then
                Me.Width = New System.Web.UI.WebControls.Unit(tableWidth, System.Web.UI.WebControls.UnitType.Pixel)
                Me.Height = New System.Web.UI.WebControls.Unit(tableHeight, System.Web.UI.WebControls.UnitType.Pixel)
                contentDiv.Style(System.Web.UI.HtmlTextWriterStyle.Width) = Me.Width.ToString()
                contentDiv.Style(System.Web.UI.HtmlTextWriterStyle.Height) = Me.Height.ToString()
                resultsTable.Style(System.Web.UI.HtmlTextWriterStyle.Width) = Me.Width.ToString()
                resultsTable.Style(System.Web.UI.HtmlTextWriterStyle.Height) = Me.Height.ToString()
            End If

            If Me.Page.IsCallback Then
                ' Apply the size on the client by passing the calculated dimensions to the task's size property
                Dim resizePanelJavaScript As String = "$find('{0}').set_size('{1}', '{2}')"
                resizePanelJavaScript = String.Format(resizePanelJavaScript, Me.ClientID, Me.Width.ToString(), Me.Height.ToString())
                Dim resizePanelCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(resizePanelJavaScript)
            End If

        End Sub

#End Region

#Region "Public Methods"

        ' Updates the panel contents with the data of the passed-in layer.
        Public Sub SetLayer(ByVal featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer, ByVal resourceName As String, ByVal mapID As String)
            ' Validate method input
            If featureGraphicsLayer Is Nothing OrElse String.IsNullOrEmpty(resourceName) OrElse String.IsNullOrEmpty(mapID) Then
                Return
            End If

            ' Set the member variable holding a reference to the panel's GraphicsLayer
            _resultsGraphicsLayer = featureGraphicsLayer

            ' Set private properties
            Me.ResourceName = resourceName
            Me.LayerName = featureGraphicsLayer.TableName
            Me.MapID = mapID

            ' Call CreateChildControls to reconstruct the panel's contents
            Me.CreateChildControls()

            Me.Refresh()
        End Sub

        ' Adds the JavaScript necessary to initialize the panel on the client to the panel's callback results 
        ' collection.  Necessary when a panel is being dynamically added at run time.
        Public Sub InitializeOnClient(ByVal parentControl As System.Web.UI.WebControls.WebControl, ByVal callbackFunctionString As String)
            ' Get the URLs of the images for the panel's expand and collapse buttons
            Dim expandedImageUrl As String = ESRI.ArcGIS.ADF.Web.UI.WebControls.ResourceUtility.GetImage("collapse.png", Me, GetType(ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanel), "Runtime")
            Dim collapsedImageUrl As String = ESRI.ArcGIS.ADF.Web.UI.WebControls.ResourceUtility.GetImage("expand.png", Me, GetType(ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanel), "Runtime")

            ' Get the panel's HTML and remove characters that will prevent it from being interpreted correctly
            ' on the client.
            Dim taskResultsPanelHtml As String = Me.GetControlHtml(Me)
            taskResultsPanelHtml = taskResultsPanelHtml.Replace("""", "\""")
            taskResultsPanelHtml = taskResultsPanelHtml.Replace(Constants.vbCr, "")
            taskResultsPanelHtml = taskResultsPanelHtml.Replace(Constants.vbLf, "")

            ' Construct JavaScript to update the panel's HTML and create an AJAX component for the panel
            Dim initTaskResultsPanelJavaScript As String = "" & ControlChars.CrLf & "                var parentControl = $get('{0}');" & ControlChars.CrLf & "                var row = parentControl.insertRow(parentControl.rows.length);" & ControlChars.CrLf & "                var cell = row.insertCell(0);" & ControlChars.CrLf & "                cell.innerHTML = ""{1}"";" & ControlChars.CrLf & "                var taskResultsPanel = $create(ESRI.ADF.Samples.CustomTasks.TaskResultsPanel,{{ 'id':'{2}', " & ControlChars.CrLf & "                    'transparency':{3},'callbackFunctionString':""{4}"", 'forcePNG':true, 'isDocked':false, " & ControlChars.CrLf & "                    'initialMaxWidth':'{5}', 'initialMaxHeight':'{6}', 'expandedImage':'{7}', " & ControlChars.CrLf & "                    'collapsedImage':'{8}' }},null,null, $get('{2}'));"
            initTaskResultsPanelJavaScript = String.Format(initTaskResultsPanelJavaScript, parentControl.ClientID, taskResultsPanelHtml, Me.ClientID, Me.Transparency, callbackFunctionString, Me.InitialMaxWidth.ToString(), Me.InitialMaxHeight.ToString(), expandedImageUrl, collapsedImageUrl)

            ' Embed the JavaScript in a callback result and add it to the panel's collection
            Dim initTaskResultsPanelCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(initTaskResultsPanelJavaScript)

            Me.CallbackResults.Add(initTaskResultsPanelCallbackResult)
        End Sub

        ' Registers JavaScript files required by the class.  This method allows external classes to register these
        ' scripts in situations where this class will not be able to (i.e. when TaskResultsPanels are only created
        ' dynamically at run time)
        Public Shared Sub RegisterScripts(ByVal registeringControl As ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl)
            If (Not registeringControl.Page.ClientScript.IsStartupScriptRegistered("TaskResultsPanelScript")) Then
                registeringControl.Page.ClientScript.RegisterStartupScript(registeringControl.GetType(), "TaskResultsPanelScript", FindNearTask_VBNet.ResourceUtility.GetJavascript(registeringControl, "TaskResultsPanel.js", registeringControl.GetType()))
            End If
        End Sub

#End Region

#Region "Private Methods"

#Region "Table Creation Methods"

        Private Function GetDisplayColumnIndexes() As System.Collections.Generic.List(Of Integer)
            Dim DisplayColumnIndexList As New System.Collections.Generic.List(Of Integer)()

            ' Get the contents template for the panel's associated GraphicsLayer.  This will contain
            ' information about the layer's display columns.
            Dim layerContentsTemplate As String = Me.GraphicsLayer.GetContentsTemplate(True, System.Drawing.Color.White, True, Nothing)

            ' Iterate through the GraphicsLayer's columns building a list of the indexes of those that
            ' are to be displayed
            For i As Integer = 0 To Me.GraphicsLayer.Columns.Count - 1
                ' Get the curernt column
                Dim currentColumn As System.Data.DataColumn = Me.GraphicsLayer.Columns(i)

                ' Get the column's visibility from its extended properties
                Dim visibility As String = TryCast(currentColumn.ExtendedProperties(ESRI.ArcGIS.ADF.Web.Constants.ADFVisibility), String)

                ' Get the display name of the field
                Dim fieldName As String = If((Not String.IsNullOrEmpty(currentColumn.Caption)), currentColumn.Caption, currentColumn.ColumnName)

                ' If the column is not supposed to be displayed, skip to the next
                If (visibility IsNot Nothing AndAlso (Not System.Convert.ToBoolean(visibility))) OrElse currentColumn.DataType Is GetType(ESRI.ArcGIS.ADF.Web.Geometry.Geometry) OrElse ((Not layerContentsTemplate.Contains(fieldName))) Then
                    Continue For
                End If

                ' Add the current column index to the list
                DisplayColumnIndexList.Add(i)
            Next i

            Return DisplayColumnIndexList
        End Function

        ' Creates the header row for the panel's data table
        Private Function CreateHeaderRow(ByVal displayColumnIndexList As System.Collections.Generic.List(Of Integer), ByRef columnWidthList As System.Collections.Generic.List(Of Single), ByRef rowHeightList As System.Collections.Generic.List(Of Single)) As System.Web.UI.WebControls.TableRow
            Dim tableRow As New System.Web.UI.WebControls.TableRow()

            ' Create the column for checkoxes to allow for selection/deselection of results
            If Me.GraphicsLayer IsNot Nothing Then
                ' Initialize a cell for the column header
                Dim tableCell As New System.Web.UI.WebControls.TableCell()
                tableCell.Style("border-right") = "silver 2px solid"
                tableCell.Style("border-bottom") = "silver 2px solid"
                tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
                ' Copy the task's font to the cell
                Me.CopyFont(tableCell.Font, Me.Font)
                tableCell.Font.Bold = True
                tableCell.Text = "Selected"

                ' Add the cell to the header row
                tableRow.Cells.Add(tableCell)

                ' Get the width and height of the text in the cell and add these to the row height
                ' and column width lists
                Dim cellSize As System.Drawing.SizeF = Me.GetTextScreenSize(tableCell.Text, tableCell.Font)
                rowHeightList.Add(cellSize.Height)
                columnWidthList.Add(cellSize.Width)
            End If

            ' Loop through the display column indexes, making a header cell for each
            For Each index As Integer In displayColumnIndexList
                ' Initialize a cell for the column header
                Dim tableCell As New System.Web.UI.WebControls.TableCell()
                tableCell.Style("border-right") = "silver 2px solid"
                tableCell.Style("border-bottom") = "silver 2px solid"
                tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
                ' Copy the task's font to the cell
                Me.CopyFont(tableCell.Font, Me.Font)
                tableCell.Font.Bold = True

                ' Add the cell to the header row
                tableRow.Cells.Add(tableCell)

                ' If the current column has a caption, use that as the header text.  Otherwise, use the 
                ' column name.  We do this because the Web ADF stores display aliases column captions.
                Dim currentColumn As System.Data.DataColumn = Me.GraphicsLayer.Columns(index)
                tableCell.Text = If((String.IsNullOrEmpty(currentColumn.Caption)), currentColumn.ColumnName, currentColumn.Caption)

                ' Get the width and height of the text in the cell and add these to the row height
                ' and column width lists
                Dim cellSize As System.Drawing.SizeF = Me.GetTextScreenSize(tableCell.Text, tableCell.Font)
                rowHeightList(0) = If(cellSize.Height > rowHeightList(0), cellSize.Height, rowHeightList(0))
                columnWidthList.Add(cellSize.Width)
            Next index
            Return tableRow
        End Function

        ' Creates the data rows for the panel's data table
        Private Function CreateDataRows(ByVal displayColumnIndexList As System.Collections.Generic.List(Of Integer), ByRef columnWidthList As System.Collections.Generic.List(Of Single), ByRef rowHeightList As System.Collections.Generic.List(Of Single)) As System.Web.UI.WebControls.TableRow()
            ' JavaScript to highlight and un-highlight the table row and corresponding graphic feature.  
            ' Wired to mouseover and mouseout.
            Dim setHighlightJavaScript As String = "" & ControlChars.CrLf & "                try {{" & ControlChars.CrLf & "                    this.style.backgroundColor = '{0}';" & ControlChars.CrLf & "                    var graphicFeatureGroup = $find('{1}');" & ControlChars.CrLf & "                    var graphicFeature = graphicFeatureGroup.get({2});" & ControlChars.CrLf & "                    graphicFeature.set_highlight({3});" & ControlChars.CrLf & "                }}" & ControlChars.CrLf & "                catch (ex) {{" & ControlChars.CrLf & "                }}"

            ' JavaScript to zoom to the feature corresponding to the clicked row.
            Dim zoomToFeatureJavaScript As String = "" & ControlChars.CrLf & "                try {{" & ControlChars.CrLf & "                    // Get the envelope of the feature" & ControlChars.CrLf & "                    var map = $find('{0}');" & ControlChars.CrLf & "                    var graphicFeatureGroup = $find('{1}');" & ControlChars.CrLf & "                    var graphicFeature = graphicFeatureGroup.get({2});" & ControlChars.CrLf & "                    var geometry = graphicFeature.get_geometry();" & ControlChars.CrLf & "                    var envelope = geometry.getEnvelope();" & ControlChars.CrLf & ControlChars.CrLf & "                    // Expand the envelope so some area round the feature is included" & ControlChars.CrLf & "                    var envelopeDivisor = 3;" & ControlChars.CrLf & "                    var xExpansionFactor = envelope.get_width() / envelopeDivisor;" & ControlChars.CrLf & "                    var yExpansionFactor = envelope.get_height() / envelopeDivisor;" & ControlChars.CrLf & ControlChars.CrLf & "                    // Update the envelope with the expansion factor" & ControlChars.CrLf & "                    var xMax = envelope.get_xmax() + xExpansionFactor;" & ControlChars.CrLf & "                    envelope.set_xmax(xMax);" & ControlChars.CrLf & ControlChars.CrLf & "                    var xMin = envelope.get_xmin() - xExpansionFactor;" & ControlChars.CrLf & "                    envelope.set_xmin(xMin);" & ControlChars.CrLf & ControlChars.CrLf & "                    var yMax = envelope.get_ymax() + yExpansionFactor;" & ControlChars.CrLf & "                    envelope.set_ymax(yMax);" & ControlChars.CrLf & ControlChars.CrLf & "                    var yMin = envelope.get_ymin() - yExpansionFactor;" & ControlChars.CrLf & "                    envelope.set_ymin(yMin);" & ControlChars.CrLf & ControlChars.CrLf & "                    // Zoom the map to the envelope" & ControlChars.CrLf & "                    map.zoomToBox(envelope, false);" & ControlChars.CrLf & "                }}" & ControlChars.CrLf & "                catch (ex) {{" & ControlChars.CrLf & "                }}"

            ' JavaScript to toggle whether or not a feature is selected.  Wired to each checkbox's onclick event.
            Dim toggleFeatureJavaScript As String = "" & ControlChars.CrLf & "                try {{" & ControlChars.CrLf & "                    var graphicFeatureGroup = $find('{0}');" & ControlChars.CrLf & "                    var graphicFeature = graphicFeatureGroup.get({1});" & ControlChars.CrLf & "                    graphicFeature.set_isSelected($get('{2}').checked);" & ControlChars.CrLf & "                }}" & ControlChars.CrLf & "                catch (ex) {{" & ControlChars.CrLf & "                }}"

            ' Get the client ID of the GraphicsLayer associated with the panel
            Dim graphicsLayerClientID As String = Me.MapInstance.GetGraphicsLayerClientID(Me.GraphicsLayer)
            Dim tableRowArray(Me.GraphicsLayer.Rows.Count - 1) As System.Web.UI.WebControls.TableRow
            Dim tableRow As System.Web.UI.WebControls.TableRow

            ' Loop through the rows (i.e. features) in the GraphicsLayer, creating a row in the rows array for each
            For i As Integer = 0 To Me.GraphicsLayer.Rows.Count - 1
                ' Create a new row and add it to the rows array
                tableRow = New System.Web.UI.WebControls.TableRow()
                tableRowArray(i) = tableRow

                ' Wire the row's onmouseover and onmouseout events to the JavaScript that applies and removes row and
                ' feature highlighting
                tableRow.Attributes.Add("onmouseover", String.Format(setHighlightJavaScript, "gray", graphicsLayerClientID, i, "true"))
                tableRow.Attributes.Add("onmouseout", String.Format(setHighlightJavaScript, "white", graphicsLayerClientID, i, "false"))

                ' Wire the row's onclick event to the JavaScript that will zoom the map to the coresponding graphic feature
                tableRow.Attributes.Add("onclick", String.Format(zoomToFeatureJavaScript, Me.MapInstance.ClientID, graphicsLayerClientID, i))

                ' Change the mouse cursor to ta pointer when the row is hovered over so users know clicking it will do
                ' something
                tableRow.Style(System.Web.UI.HtmlTextWriterStyle.Cursor) = "pointer"

                ' Get the current row (feature) from the GraphicsLayer
                Dim currentRow As System.Data.DataRow = Me.GraphicsLayer.Rows(i)

                ' Create a cell to hold the checkbox that toggles whether the corresponding feature is selected
                If GraphicsLayer IsNot Nothing Then
                    ' Instantiate and initialize the styling of the cell
                    Dim tableCell As New System.Web.UI.WebControls.TableCell()
                    tableCell.Style("border-right") = "silver 2px solid"
                    tableCell.Style("border-bottom") = "silver 2px solid"
                    tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"

                    ' Add the cell to the current row
                    tableRow.Cells.Add(tableCell)

                    ' Create a checkbox and set its ID to the current row index.  This row index corresponds to 
                    ' the ID of the graphic feature required to retrieve that feature through the Web ADF JavaScript
                    ' GraphicFeatureGroup::get function.
                    Dim checkBox As New System.Web.UI.HtmlControls.HtmlInputCheckBox()
                    checkBox.ID = i.ToString()

                    ' Set the checked state of the checkbox based on the selected state of the current feature
                    checkBox.Checked = System.Convert.ToBoolean(currentRow(GraphicsLayer.IsSelectedColumn))

                    ' Wire the JavaScript to toggle the selected state of the corresponding feature to the 
                    ' checkbox's onclick event
                    Dim clientAction As String = String.Format(toggleFeatureJavaScript, graphicsLayerClientID, i, checkBox.ClientID)
                    checkBox.Attributes.Add("onclick", clientAction)

                    ' Add the checkbox to the current cell
                    tableCell.Controls.Add(checkBox)
                End If

                ' Initialize column index and row index variables that are used to traverse the lists containing
                ' the table's column widths and row heights.
                Dim columnIndex As Integer = 1
                Dim rowIndex As Integer = i + 1

                ' Loop through the indexes of the display columns creating a table cell and adding the 
                ' corresponding feature data for each
                For Each index As Integer In displayColumnIndexList
                    ' Instantiate the cell and initialize its styling
                    Dim tableCell As New System.Web.UI.WebControls.TableCell()
                    tableCell.Style("border-right") = "silver 2px solid"
                    tableCell.Style("border-bottom") = "silver 2px solid"
                    tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"

                    ' Apply the task's font to the cell
                    Me.CopyFont(tableCell.Font, Me.Font)
                    tableCell.Font.Bold = False

                    ' Add the cell to the current row
                    tableRow.Cells.Add(tableCell)

                    ' Set the cell's text to the value of the corresponding attribute for the current feature
                    tableCell.Text = currentRow(index).ToString()

                    ' Add content to empty cells so they are still formatted
                    If String.IsNullOrEmpty(tableCell.Text) Then
                        tableCell.Text = "&nbsp;"
                    End If

                    ' Get the size of the cell given its text
                    Dim cellSize As System.Drawing.SizeF = Me.GetTextScreenSize(tableCell.Text, tableCell.Font)

                    ' Only update the row height if the current row height has not been initialized or the height 
                    ' of the current cell is greater than the stored row height
                    If columnIndex = 1 Then
                        rowHeightList.Add(cellSize.Height)
                    Else
                        rowHeightList(rowIndex) = If(cellSize.Height > rowHeightList(rowIndex), cellSize.Height, rowHeightList(rowIndex))
                    End If

                    ' Only update the column width if the width of the current cell is greater than the stored 
                    ' column width
                    columnWidthList(columnIndex) = If(cellSize.Width > columnWidthList(columnIndex), cellSize.Width, columnWidthList(columnIndex))

                    columnIndex += 1
                Next index
            Next i

            Return tableRowArray
        End Function

#End Region

        ' Retrieves the GraphicsLayer associated with the panel based on the MapInstance, ResourceName, and
        ' LayerName properties
        Private Function GetGraphicsLayer() As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer
            Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = Nothing

            ' Make sure a resource name and layer name have been defined.  These are initialized in SetLayer.
            If (Not String.IsNullOrEmpty(Me.ResourceName)) AndAlso (Not String.IsNullOrEmpty(Me.LayerName)) Then
                ' Get the resource item corresponding to the resource name
                Dim mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = Me.MapInstance.MapResourceManagerInstance.ResourceItems.Find(Me.ResourceName)

                ' Make sure the resource item was found
                If mapResourceItem IsNot Nothing AndAlso mapResourceItem.Resource IsNot Nothing Then
                    ' Get a reference to the resource underlying the resource item as a graphics resource
                    Dim graphicsResource As ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapResource = TryCast(mapResourceItem.Resource, ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapResource)

                    ' Make sure the resource could be referenced as a graphics resource
                    If graphicsResource IsNot Nothing Then
                        ' Loop through the tables and find the one matching the panel's layer name.  This will be
                        ' the associated GraphicsLayer.
                        For Each table As System.Data.DataTable In graphicsResource.Graphics.Tables
                            If table.TableName = Me.LayerName Then
                                featureGraphicsLayer = TryCast(table, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)
                                Exit For
                            End If
                        Next table
                    End If
                End If
            End If

            Return featureGraphicsLayer
        End Function

        ' Gets the screen size of the passed-in text in the passed-in font
        Private Function GetTextScreenSize(ByVal text As String, ByVal fontInfo As System.Web.UI.WebControls.FontInfo) As System.Drawing.SizeF
            Dim size As System.Drawing.SizeF
            Dim emSize As Single = System.Convert.ToSingle(fontInfo.Size.Unit.Value + 1)
            emSize = If(emSize = 0, 12, emSize)

            Dim font As New System.Drawing.Font(fontInfo.Name, emSize)
            Dim bitmap As New System.Drawing.Bitmap(1000, 100)
            Dim graphics As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bitmap)

            size = graphics.MeasureString(text, font)
            graphics.Dispose()
            Return size
        End Function

        ' Copies the properties of one FontInfo to another
        Private Sub CopyFont(ByVal targetFont As System.Web.UI.WebControls.FontInfo, ByVal sourceFont As System.Web.UI.WebControls.FontInfo)
            targetFont.Bold = sourceFont.Bold
            targetFont.Italic = sourceFont.Italic
            targetFont.Name = sourceFont.Name
            targetFont.Names = sourceFont.Names
            targetFont.Overline = sourceFont.Overline
            targetFont.Size = sourceFont.Size
            targetFont.Strikeout = sourceFont.Strikeout
            targetFont.Underline = sourceFont.Underline
        End Sub

        ' Retrieves the HTML of a WebControl
        Private Function GetControlHtml(ByVal webControl As System.Web.UI.Control) As String
            ' Instantaite an HtmlTextWriter object
            Dim stringWriter As New System.IO.StringWriter()
            Dim htmlTextWriter As New System.Web.UI.HtmlTextWriter(stringWriter)

            ' Render the passed-in control's html to the HtmlTextWriter
            webControl.RenderControl(htmlTextWriter)

            ' Get the control's html as a string
            Return stringWriter.ToString()
        End Function

#End Region
    End Class
End Namespace