Common PostbackManager
Common_PostbackManager_VBNet\PostbackManagerWebSite\GetAttributeTable.aspx.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
Public Partial Class GetAttributeTable
  Inherits System.Web.UI.Page
  #Region "Page Member Variables"

  ' Whether attributes are to be retrieved for the clicked layer
  Private _getLayerAttributes As Boolean = False
  ' The number of records displayed at one time on the attribute table
  Private _pageSize As Integer = 20

  #End Region

  #Region "ASP.NET Page Life Cycle Event Handlers - Page_Load"

  ' Wires event handlers
  Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    AddHandler PostbackManager1.RequestReceived, AddressOf PostbackManager1_RequestReceived
    AddHandler Toc1.NodeClicked, AddressOf Toc1_NodeClicked
    AddHandler Toc1.NodesPopulated, AddressOf Toc1_NodesPopulated
  End Sub

  #End Region

  #Region "Web ADF Control Event Handlers - Toc1_NodesPopulated, Toc1_NodeClicked"

  ' Fires once the TOC's nodes have been initialized
  Private Sub Toc1_NodesPopulated(ByVal sender As Object, ByVal e As System.EventArgs)
    ' Find which nodes correspond to a layer and mark them with an HTML attribute
    For Each node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In Toc1.Nodes
      Me.MarkLayerNodes(node)
    Next node
  End Sub

  ' Fires when a TOC node is clicked
  Private Sub Toc1_NodeClicked(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs)
    ' Check whether layer attributes are to be retrieved.  This value is set in 
    ' PostbackManager1_RequestReceived
    If (Not _getLayerAttributes) Then
      Return
    End If

    ' Get the TocLayer corresponding to the clicked node
    Dim selectedLayer As ESRI.ArcGIS.ADF.Web.TocLayer = TryCast(args.Node.Data, ESRI.ArcGIS.ADF.Web.TocLayer)

    ' Get the data frame node that contains the layer node
    Dim dataFrameNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = Me.GetParentDataFrameNode(args.Node)

    ' Create a data table containing the clicked layer's attributes
    Dim attributeTable As System.Data.DataTable = Me.GetLayerAttributeData(Map1, dataFrameNode.Text, selectedLayer.ID)

    ' Apply the layer's LayerFormat to the attribute table.  This will set properties such as field
    ' visibilities and display names.
    Dim layerFormat As ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat = ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(MapResourceManager1, dataFrameNode.Text, selectedLayer.ID)
    layerFormat.Apply(attributeTable)

    ' Store the table in session for quick retrieval during paging or sorting
    Me.Session("AttributeTable") = attributeTable

    ' Calculate the number of pages the displayed table will have and store the number in session
    Dim pageCount As Integer = CInt(Fix(System.Math.Round((CDbl(attributeTable.Rows.Count) / CDbl(_pageSize)) +.5)))
    Me.Session("PageCount") = pageCount

    ' Display the table's first page
    Me.ShowAttributeTable(1)

    ' Copy the callback results of the floating panel containing the table to the TOC so the
    ' results that will display the table are processed on the client.
    Toc1.CallbackResults.CopyFrom(FloatingPanel1.CallbackResults)

    ' Pass the name of the clicked layer back to the client via PostbackManager's CustomResults property.
    ' This will be used to update the floating panel's title.
    PostbackManager1.CustomResults = args.Node.Text
  End Sub

  #End Region

  #Region "Custom Control Event Handlers - PostbackManager1_RequestReceived"

  Private Sub PostbackManager1_RequestReceived(ByVal sender As Object, ByVal args As PostbackManager_VBNet.AdfRequestEventArgs)
    ' Get the request's arguments and extract EventArg
    Dim requestArgs As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.ParseStringIntoNameValueCollection(args.RequestArguments, True)
    Dim eventArg As String = requestArgs("EventArg")

    ' Check whether the request was initiated by the TOC
    If args.CallingControl.ID = Toc1.ID Then
      ' Check whether the request includes the custom argument indicating that layer attributes are to be
      ' retrieved.  This argument is added to the request on the client via onAdfRequest, which is a handler for
      ' the PostbackManager's client-side invokingRequest event.
      If Not requestArgs("getAttributes") Is Nothing Then
        _getLayerAttributes = True
      End If
    ' Check whether the request was initiated by paging or sorting the attribute table
    ElseIf eventArg = "nextPage" OrElse eventArg="previousPage" OrElse eventArg="firstPage" OrElse eventArg="lastPage" OrElse eventArg="sort" Then
      ' Get the current page number
      Dim pageNumber As Integer = CInt(Fix(Me.Session("PageNumber")))
      Select Case eventArg
        ' Set the page number if the request is for paging.  If it is for sorting, call the SortAttributeTable method
        Case "nextPage" 
          pageNumber += 1
        Case "previousPage" 
          pageNumber -= 1
        Case "firstPage" 
          pageNumber = 1
        Case "lastPage" 
          pageNumber = CInt(Fix(Me.Session("PageCount")))
        Case "sort" 
          Me.SortAttributeTable(requestArgs("Column"))
      End Select
      ' Update the displayed table to show the specified page
      Me.ShowAttributeTable(pageNumber)

      ' Copy the callback results of the floating panel containing the attribute table to the PostbackManager so the
      ' updates to the table display are processed on the client.
      PostbackManager1.CallbackResults.CopyFrom(FloatingPanel1.CallbackResults)
    End If
  End Sub

  #End Region

  #Region "Private Page Methods"

  #Region "TOC Node Manipulation - MarkLayerNodes, GetParentDataFrameNode"

  ' Finds TOC layer nodes that are descendants of the passed-in node and adds an HTML attribute to 
  ' them marking them as such
  Private Sub MarkLayerNodes(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode)
    ' Check whether the passed-in node is a layer node and add an "IsLayer" attribute to it if so
    If TypeOf node.Data Is ESRI.ArcGIS.ADF.Web.TocLayer Then
      node.Attributes.Add("IsLayer", "true")
    End If

    ' Recursively call the method on the passed-in node's child nodes
    For Each childNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In node.Nodes
      Me.MarkLayerNodes(childNode)
    Next childNode
  End Sub

  ' Retrieves the TOC data frame node that is an ancestor of the passed-in node
  Private Function GetParentDataFrameNode(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode) As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode
    ' Declare a node variable to hold the function's return value
    Dim dataFrameNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = Nothing

    ' If the current node is a data frame node, store it in the return variable.  Otherwise, call the
    ' method recursively on the node's parent.
    If TypeOf node.Data Is ESRI.ArcGIS.ADF.Web.TocDataFrame Then
      dataFrameNode = node
    Else
      dataFrameNode = Me.GetParentDataFrameNode(TryCast(node.Parent, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode))
    End If

    Return dataFrameNode
  End Function

  #End Region

  #Region "Attribute Table Manipulation - ShowAttributeTable, PopulateAttributeTable, SortAttributeTable"

  ' Updates the AttributeTable control to display attributes on the specified page of the current layer
  Private Sub ShowAttributeTable(ByVal pageNumber As Integer)
    ' Retrieve the number of attribute pages and validate the requested page number
    Dim pageCount As Integer = CInt(Fix(Me.Session("PageCount")))
    If pageNumber = 0 OrElse pageNumber > pageCount Then
      Return
    End If

    ' Update the current page number
    Me.Session("PageNumber") = pageNumber

    ' Get the attribute DataTable for the current layer
    Dim attributeTable As System.Data.DataTable = TryCast(Me.Session("AttributeTable"), System.Data.DataTable)

    ' Calculate the first and last indexes of the rows to be displayed
    Dim startIndex As Integer = (pageNumber - 1) * _pageSize
    Dim stopIndex As Integer = (pageNumber * _pageSize) - 1
    If stopIndex >= attributeTable.Rows.Count Then
      stopIndex = attributeTable.Rows.Count - 1
    End If

    ' Update the attribute table display
    Me.PopulateAttributeTable(attributeTable, startIndex, stopIndex)

    ' Update the paging text
    PageLabel.Text = String.Format("Current Page: {0} of {1}", pageNumber, pageCount)

    ' Make sure the floating panel containing the attribute table is visible, then refresh it to
    ' apply the updates to the attribute table control
    FloatingPanel1.ShowFloatingPanel()
    FloatingPanel1.Refresh()
  End Sub

  ' Displays the data specified by the method parameters on the AttributeTable control
  Private Sub PopulateAttributeTable(ByVal dataTable As System.Data.DataTable, ByVal startIndex As Integer, ByVal stopIndex As Integer)
    ' Remove all rows from the display table
    AttributeTable.Rows.Clear()
    AttributeTable.GridLines = System.Web.UI.WebControls.GridLines.Both

    ' Create a row for the header row and add it to the display table
    Dim row As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow()
    AttributeTable.Rows.Add(row)
    Dim cell As System.Web.UI.WebControls.TableCell = Nothing

    ' Add the name of each visible field to the header row
    For Each column As System.Data.DataColumn In dataTable.Columns
      ' Check whether the current column is visible
      If System.Convert.ToBoolean(column.ExtendedProperties(ESRI.ArcGIS.ADF.Web.Constants.ADFVisibility)) Then
        ' Create a cell for the column header and apply a bold font to it
        cell = New System.Web.UI.WebControls.TableCell()
        cell.Font.Bold = True

        ' Set the header text to either the field's display name or its database name if a display name is 
        ' not specified
        Dim headerText As String
        If (String.IsNullOrEmpty(column.Caption)) Then
          headerText = column.ColumnName
        Else
          headerText = column.Caption
        End If

        ' Create a hyperlink for the header - we do this to make the table sortable by clicking a header cell
        Dim sortLink As System.Web.UI.HtmlControls.HtmlAnchor = New System.Web.UI.HtmlControls.HtmlAnchor()
        sortLink.InnerText = headerText

        ' Set the header link to issue an asynchronous request to the server via PostbackManager's doAsyncRequest
        sortLink.HRef = String.Format("javascript:ESRI.ADF.Samples.PostbackManager.doAsyncRequest('EventArg=sort&Column={0}');", headerText)
        cell.Controls.Add(sortLink)
        row.Cells.Add(cell)
      End If
    Next column

    ' Add the data rows to the display table
    Dim i As Integer = startIndex
    Do While i <= stopIndex
      ' Add a row to the display table
      row = New System.Web.UI.WebControls.TableRow()
      AttributeTable.Rows.Add(row)

      ' Get the current row from the data table
      Dim dataRow As System.Data.DataRow = dataTable.Rows(i)

      ' Add a new cell for each visible column and populate it with that column's value
      For Each column As System.Data.DataColumn In dataTable.Columns
        ' Check whether the current field is visible
        If System.Convert.ToBoolean(column.ExtendedProperties(ESRI.ArcGIS.ADF.Web.Constants.ADFVisibility)) Then
          ' Set the cell's text to be the value of the current column in the current row
          cell = New System.Web.UI.WebControls.TableCell()
          cell.Text = System.Convert.ToString(dataRow(column.ColumnName)).Trim()

          ' Set a tab as the cell's text if there is no current value.  Without this, the cell borders do
          ' not render in this situation.
          If String.IsNullOrEmpty(cell.Text) Then
            cell.Text = "&nbsp;"
          End If
          row.Cells.Add(cell)
        End If
      Next column
      i += 1
    Loop
  End Sub

  ' Sorts the display table on the specified column
  Private Sub SortAttributeTable(ByVal columnName As String)
    ' Get the current data table from session
    Dim attributeTable As System.Data.DataTable = TryCast(Me.Session("AttributeTable"), System.Data.DataTable)

    ' If the column has a display name specified, retrieve the database field name
    If (Not attributeTable.Columns.Contains(columnName)) Then
      For Each column As System.Data.DataColumn In attributeTable.Columns
        If column.Caption = columnName Then
          columnName = column.ColumnName
          Exit For
        End If
      Next column
    End If

    ' Get the current sort direction
    Dim currentSort As String = TryCast(Me.Session("CurrentSort"), String)

    ' Create a sort expression that will sort on the passed-in column in the opposite
    ' direction of the current sort
    Dim sortExpression As String
    If (currentSort = String.Format("{0} ASC", columnName)) Then
      sortExpression = String.Format("{0} DESC", columnName)
    Else
      sortExpression = String.Format("{0} ASC", columnName)
    End If

    ' Update the current sort expression stored in session
    Me.Session("CurrentSort") = sortExpression

    ' Apply the sort
    attributeTable.DefaultView.Sort = sortExpression

    ' Replace the current data table with the sorted table
    Me.Session("AttributeTable") = attributeTable.DefaultView.ToTable()
  End Sub

  #End Region

  ' Retrieves attribute data for the specified layer
  Private Function GetLayerAttributeData(ByVal adfMap As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map, ByVal resourceName As String, ByVal layerID As String) As System.Data.DataTable
    ' Get the map functionality for the resource
    Dim mapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = adfMap.GetFunctionality(resourceName)

    ' Get the resource and make sure it supports querying
    Dim gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = mapFunctionality.Resource
    Dim supportsQueries As Boolean = gisResource.SupportsFunctionality (GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality))
    If (Not supportsQueries) Then
      Return Nothing
    End If

    ' Get query functionality for the resource
    Dim queryFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = TryCast(gisResource.CreateFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), Nothing), ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)

    ' Initialize a query filter.  We do not specify a where clause so all features are returned. 
    ' We also specify that geometry not be returned since only the attribute data will be used and
    ' retrieving geometry is a relatively expensive operation.
    Dim adfQueryFilter As ESRI.ArcGIS.ADF.Web.QueryFilter = New ESRI.ArcGIS.ADF.Web.QueryFilter()
    adfQueryFilter.ReturnADFGeometries = False
    adfQueryFilter.MaxRecords = 10000

    ' Execute the query, returning the result
    Return queryFunctionality.Query(mapFunctionality.Name, layerID, adfQueryFilter)
  End Function

  #End Region
End Class