Common Custom data source
Common_CustomDataSource_VBNet\REXMLDataSource_VBNet\QueryFunctionality.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
Namespace REXMLDataSource_VBNet
    ' Along with IGISDataSource and IGISResource, IGISFunctionality is one of the three required
    ' interfaces for any Web ADF Data Source implementation.  This interface is responsible for
    ' providing members to functionally interact with the underlying data.  Essentially, an 
    ' IGISFunctionality implementation can be thought of as describing what can be done with the 
    ' data.
    '
    ' This particular implementation inherits from IQueryFunctionality, which implements 
    ' IGISFunctionality.  IQueryFunctionality provides methods and properties that allow the
    ' extraction of attributes and geometry from the underlying data source via query expressions
    ' and geometries.
    Public Class QueryFunctionality
        Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality
#Region "Instance Variable Declarations"

        Private m_name As String = String.Empty
        Private m_gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = Nothing
        Private m_webControl As System.Web.UI.WebControls.WebControl
        Private m_initialized As Boolean = False


#End Region

#Region "Constructor "

        Public Sub New(ByVal name As String, ByVal resource As REXMLDataSource_VBNet.MapResource)
            m_name = name
            m_gisResource = resource
        End Sub

#End Region

#Region "REXML QueryFunctionality Members"

        ' Enables convenient retrieval of the graphics dataset underlying either (a) the resource 
        ' associated with the QueryFunctionality instance (if no functionality name is passed into 
        ' the function) or (b) the specific MapFunctionality indicated by the mapFunctionalityName 
        ' parameter
        Private Function GetGraphicsDataSet(ByVal mapFunctionalityName As String) As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet
            Dim mapResource As REXMLDataSource_VBNet.MapResource = TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)
            If mapResource Is Nothing Then
                Return Nothing
            End If

            ' If no functionality name was passed in, return the graphics dataset underlying the REXML
            ' MapResource associated with the QueryFunctionality instance.  Otherwise, retrieve the
            ' REXML MapFunctionality indicated by the passed-in name and return the graphics dataset
            ' underlying that functionality.
            If mapFunctionalityName Is Nothing Then
                Return mapResource.Graphics
            Else
                Dim mapFunctionality As REXMLDataSource_VBNet.MapFunctionality = TryCast(mapResource.Functionalities.Find(mapFunctionalityName), REXMLDataSource_VBNet.MapFunctionality)
                If mapFunctionality Is Nothing Then
                    Return Nothing
                Else
                    Return mapFunctionality.GraphicsDataSet
                End If
            End If
        End Function

        ' Enables convenient retrieval of the graphics layer with the specified ID from the REXML
        ' MapFunctionality with the specified name
        Private Function GetGraphicsLayer(ByVal mapFunctionalityName As String, ByVal layerID As String) As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer
            ' Make sure a layerID was specified
            If layerID Is Nothing Then
                Return Nothing
            End If

            ' Retrieve the graphics dataset from the REXML MapFunctionality with the passed-in name
            Dim graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet = GetGraphicsDataSet(mapFunctionalityName)

            ' Make sure a graphics dataset was found
            If graphicsDataSet Is Nothing Then
                Return Nothing
            End If

            ' Return the table in the graphics dataset that has a name matching the passed-in layer ID 
            ' as a Web ADF GraphicsLayer
            Return TryCast(graphicsDataSet.Tables(layerID), ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
        End Function

        ' Copies records satisfying a Web ADF Query Filter from a graphics layer to a data table.
        ' Also returns a list of the geometries for the rows that satsify the query
        Private Function InitialAttributeQuery(ByVal graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer, ByVal resultsDataTable As System.Data.DataTable, ByVal adfQueryFilter As ESRI.ArcGIS.ADF.Web.QueryFilter) As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geometry.Geometry)
            Dim adfGeometryList As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geometry.Geometry) = New System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geometry.Geometry)()
            Dim dataRowArray As System.Data.DataRow() = graphicsLayer.Select(adfQueryFilter.WhereClause)
            For Each dataRow As System.Data.DataRow In dataRowArray
                resultsDataTable.ImportRow(dataRow)
                adfGeometryList.Add(graphicsLayer.GeometryFromRow(dataRow))
            Next dataRow

            Return adfGeometryList
        End Function

        ' Copies records satisfying a Web ADF Spatial Filter from a graphics layer to a data table.
        Private Sub InitialSpatialQuery(ByVal graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer, ByVal resultsDataTable As System.Data.DataTable, ByVal adfSpatialFilter As ESRI.ArcGIS.ADF.Web.SpatialFilter)
            For Each dataRow As System.Data.DataRow In graphicsLayer.Rows
                Dim adfGeometry As ESRI.ArcGIS.ADF.Web.Geometry.Geometry = graphicsLayer.GeometryFromRow(dataRow)

                ' Evaluate feature geometry and spatial filter geometry.  Designed to work if the spatial 
                ' filter geometry is an Envelope - not implemented for other geometry types.  
                If (Not adfGeometry Is Nothing) AndAlso (TypeOf adfSpatialFilter.Geometry Is ESRI.ArcGIS.ADF.Web.Geometry.Envelope) AndAlso (ESRI.ArcGIS.ADF.Web.Geometry.Utility.GeometryInExtent(adfGeometry, CType(adfSpatialFilter.Geometry, ESRI.ArcGIS.ADF.Web.Geometry.Envelope))) Then
                    resultsDataTable.ImportRow(dataRow)
                End If

            Next dataRow
        End Sub

        ' Allows filtering of rows in a data table based on a Web ADF QueryFilter
        Private Sub AttributeQueryOnSpatialResults(ByVal dataTable As System.Data.DataTable, ByVal adfQueryFilter As ESRI.ArcGIS.ADF.Web.QueryFilter)
            ' The passed-in query filter has no query, so no filtering will be performed
            If adfQueryFilter.WhereClause = "" Then
                Return
            End If

            ' Get the rows from the passed-in data table that satisfy the query filter's where clause
            Dim selectionDataRowArray As System.Data.DataRow() = dataTable.Select(adfQueryFilter.WhereClause)

            ' Iterate through all the rows in the passed-in data table, removing rows that are not
            For Each dataRow As System.Data.DataRow In dataTable.Rows
                If System.Array.IndexOf(selectionDataRowArray, dataRow) = -1 Then
                    dataTable.Rows.Remove(dataRow)
                End If
            Next dataRow
        End Sub

        ' Allows filtering of rows in a data table based on whether the passed-in geometries intersect
        ' the spatial filter geometry.  The passed-in geometries are meant to be the geoemtries from
        ' the rows in the data table.
        Private Sub SpatialQueryOnAttributeResults(ByVal dataTable As System.Data.DataTable, ByVal adfSpatialFilter As ESRI.ArcGIS.ADF.Web.SpatialFilter, ByVal geometries As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geometry.Geometry))
            If geometries Is Nothing OrElse geometries.Count = 0 Then
                Return
            End If

            Dim i As Integer = 0
            Dim j As Integer = 0
            Do While i < dataTable.Rows.Count
                ' To implement, evaluate if feature geometry intersects spatial filter geometry

                'if (!GeometriesIntersect(geometries[j], sf.Geometry))
                '{
                'dataTable.Rows.Remove(dataTable.Rows[i]);
                '--i;
                '}
                i += 1
                j += 1
            Loop
        End Sub

        ' Clones the passed-in data column
        Private Function CloneColumn(ByVal sourceDataColumn As System.Data.DataColumn) As System.Data.DataColumn
            ' Create a data column instance that matches the type of the passed-in data column
            Dim clonedDataColumn As System.Data.DataColumn = CType(System.Activator.CreateInstance(sourceDataColumn.GetType()), System.Data.DataColumn)

            ' Copy the properties from the passed-in data column to the new data column
            clonedDataColumn.AllowDBNull = sourceDataColumn.AllowDBNull
            clonedDataColumn.AutoIncrement = sourceDataColumn.AutoIncrement
            clonedDataColumn.AutoIncrementStep = sourceDataColumn.AutoIncrementStep
            clonedDataColumn.AutoIncrementSeed = sourceDataColumn.AutoIncrementSeed
            clonedDataColumn.Caption = sourceDataColumn.Caption
            clonedDataColumn.ColumnName = sourceDataColumn.ColumnName
            clonedDataColumn.DataType = sourceDataColumn.DataType
            clonedDataColumn.DefaultValue = sourceDataColumn.DefaultValue
            clonedDataColumn.ColumnMapping = sourceDataColumn.ColumnMapping
            clonedDataColumn.ReadOnly = sourceDataColumn.ReadOnly
            clonedDataColumn.MaxLength = sourceDataColumn.MaxLength
            clonedDataColumn.DateTimeMode = sourceDataColumn.DateTimeMode
            clonedDataColumn.Namespace = sourceDataColumn.Namespace
            clonedDataColumn.Prefix = sourceDataColumn.Prefix
            clonedDataColumn.Unique = sourceDataColumn.Unique

            ' Copy the extended properties from the passed-in data column to the new data column
            If Not sourceDataColumn.ExtendedProperties Is Nothing Then
                For Each extendedPropertyKey As Object In sourceDataColumn.ExtendedProperties.Keys
                    clonedDataColumn.ExtendedProperties(extendedPropertyKey) = sourceDataColumn.ExtendedProperties(extendedPropertyKey)
                Next extendedPropertyKey
            End If

            Return clonedDataColumn
        End Function

        ' Gets the REXML MapFunctionality of the passed-in name from the MapResource associated with the
        ' current QueryFunctionality instance
        Private Function GetMapFunctionality(ByVal mapFunctionalityName As String) As REXMLDataSource_VBNet.MapFunctionality
            If m_gisResource Is Nothing Then
                Return Nothing
            End If
            Dim mapResource As REXMLDataSource_VBNet.MapResource = TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)
            If mapFunctionalityName Is Nothing Then
                Return Nothing
            Else
                Dim mapFunctionality As REXMLDataSource_VBNet.MapFunctionality = TryCast(mapResource.Functionalities.Find(mapFunctionalityName), REXMLDataSource_VBNet.MapFunctionality)
                Return mapFunctionality
            End If
        End Function
#End Region

#Region "IQueryFunctionality Members"

        ' Allows querying of the REXML MapFunctionality indicated by the passed-in name with the parameters
        ' specified by the passed-in FindParameters object.  More specifically, Find searches the layers
        ' and fields specified by FindParameters for values that completely or partly match the FindString
        ' of the FindParameters object.
        Public Function Find(ByVal mapFunctionalityName As String, ByVal findParameters As ESRI.ArcGIS.ADF.Web.FindParameters) As System.Data.DataTable() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Find
            Dim dataTableList As System.Collections.Generic.List(Of System.Data.DataTable) = New System.Collections.Generic.List(Of System.Data.DataTable)()
            Dim graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet = GetGraphicsDataSet(mapFunctionalityName)
            If graphicsDataSet Is Nothing Then
                Return Nothing
            End If

            ' Initialize Web ADF QueryFilter based on passed-in FindParameters
            Dim adfQueryFilter As ESRI.ArcGIS.ADF.Web.QueryFilter = New ESRI.ArcGIS.ADF.Web.QueryFilter()
            adfQueryFilter.MaxRecords = findParameters.MaxRecords
            adfQueryFilter.ReturnADFGeometries = findParameters.ReturnADFGeometries

            ' Get an enumerator of the layers and fields to be searched from the FindParameters object
            Dim dictionaryEnumerator As System.Collections.IDictionaryEnumerator = findParameters.LayersAndFields.GetEnumerator()
            Do While dictionaryEnumerator.MoveNext()
                ' Each index of the enumerator will have the layer ID as its key and a string array
                ' of field names as its value
                Dim layerID As String = TryCast(dictionaryEnumerator.Key, String)
                Dim searchFields As String() = TryCast(dictionaryEnumerator.Value, String())

                ' Iterate through the fields to be searched for the current layer and build the where clause
                Dim whereExpression As System.Text.StringBuilder = New System.Text.StringBuilder()
                Dim i As Integer = 0
                Do While i < searchFields.Length
                    If findParameters.UseSqlContains Then
                        ' todo: change to use SQL CONTAINS statement
                        whereExpression.Append(String.Format("{0} like '%{1}%'", searchFields(i), findParameters.FindString))
                    Else
                        whereExpression.Append(String.Format("{0} like '%{1}%'", searchFields(i), findParameters.FindString))
                    End If
                    If i <> searchFields.Length - 1 Then
                        whereExpression.Append(" OR ")
                    End If
                    i += 1
                Loop

                adfQueryFilter.WhereClause = whereExpression.ToString()

                ' Get the graphics layer corresponding to the current layer ID from the map functionality's
                ' graphics dataset
                Dim graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = TryCast(graphicsDataSet.Tables(layerID), ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
                If Not graphicsLayer Is Nothing Then
                    ' Make sure the current graphics layer satisfies the specified visibility option
                    If findParameters.FindOption <> ESRI.ArcGIS.ADF.Web.FindOption.VisibleLayers OrElse (findParameters.FindOption = ESRI.ArcGIS.ADF.Web.FindOption.VisibleLayers AndAlso graphicsLayer.Visible = True) Then
                        ' Execute the query for the current layer and add the results to the list of data tables
                        dataTableList.Add(Query(mapFunctionalityName, graphicsLayer.TableName, adfQueryFilter))
                    End If
                End If
            Loop

            Return dataTableList.ToArray()
        End Function

        ' Allows querying of multiple layers of a REXML MapFunctionality based on an input point
        Public Function Identify(ByVal mapFunctionalityName As String, ByVal adfGeometry As ESRI.ArcGIS.ADF.Web.Geometry.Geometry, ByVal tolerance As Integer, ByVal identifyOption As ESRI.ArcGIS.ADF.Web.IdentifyOption, ByVal layerIDs As String()) As System.Data.DataTable() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Identify
            Dim dataTableList As System.Collections.Generic.List(Of System.Data.DataTable) = New System.Collections.Generic.List(Of System.Data.DataTable)()

            ' Get the graphics dataset for the REXML MapFunctionality indicated by the passed-in name
            Dim graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet = GetGraphicsDataSet(mapFunctionalityName)
            If graphicsDataSet Is Nothing Then
                Return Nothing
            End If

            ' Get the REXML MapFunctionality object
            Dim rexmlMapFunctionality As REXMLDataSource_VBNet.MapFunctionality = GetMapFunctionality(mapFunctionalityName)

            If rexmlMapFunctionality Is Nothing Then
                Return Nothing
            End If

            ' Get the screen dimensions and the Web ADF extent of the map displaying the data source.  
            ' These will be used in calculating a search tolerance around the input point
            Dim viewWidth, viewHeight As Integer
            rexmlMapFunctionality.GetMapDimensions(viewWidth, viewHeight)
            Dim mapExtent As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = rexmlMapFunctionality.GetMapExtent()
            If mapExtent Is Nothing Then
                Return Nothing
            End If

            Dim adfSpatialFilter As ESRI.ArcGIS.ADF.Web.SpatialFilter = New ESRI.ArcGIS.ADF.Web.SpatialFilter()
            adfSpatialFilter.SearchOrder = ESRI.ArcGIS.ADF.Web.SearchOrder.Spatial

            ' Make sure the passed-in geometry is a Web ADF Point.  Other geometry types are not supported in
            ' this implementation
            If TypeOf adfGeometry Is ESRI.ArcGIS.ADF.Web.Geometry.Point Then
                ' Calculate the tolerance to add around the point for the search geometry
                Dim pixelsPerMapUnit As Double = viewWidth / mapExtent.Width
                Dim mapTolerance As Double = tolerance / pixelsPerMapUnit

                ' Create the search geometry by initializing a Web ADF Envelope with the passed-in point's
                ' coordinates plus and minus the calculated search tolerance
                Dim adfPoint As ESRI.ArcGIS.ADF.Web.Geometry.Point = TryCast(adfGeometry, ESRI.ArcGIS.ADF.Web.Geometry.Point)
                adfSpatialFilter.Geometry = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(adfPoint.X - mapTolerance, adfPoint.Y - mapTolerance, adfPoint.X + mapTolerance, adfPoint.Y + mapTolerance)

            Else
                Throw New System.NotSupportedException("GraphicsLayer only supports Points in the " & "Identify method.")
            End If

            ' Check whether any layer IDs were specified in the operation.  If so, only query those layers.  If
            ' not, query all the layers.  Add the results of each query to the list of data tables.
            If layerIDs Is Nothing Then
                For Each graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer In graphicsDataSet.Tables
                    dataTableList.Add(Query(mapFunctionalityName, graphicsLayer.TableName, adfSpatialFilter))
                Next graphicsLayer
            Else
                For Each layerID As String In layerIDs
                    dataTableList.Add(Query(mapFunctionalityName, layerID, adfSpatialFilter))
                Next layerID
            End If

            Return dataTableList.ToArray()
        End Function

        ' Executes a query based on the passed-in parameters and returns the results as a DataTable
        Public Function Query(ByVal mapFunctionalityName As String, ByVal layerID As String, ByVal adfQueryFilter As ESRI.ArcGIS.ADF.Web.QueryFilter) As System.Data.DataTable Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Query
            ' Get the graphics layer specified by the passed-in layer ID and map functionality name
            Dim graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = GetGraphicsLayer(mapFunctionalityName, layerID)

            If graphicsLayer Is Nothing Then
                Return Nothing
            End If

            ' Declare a data table.  This table will hold the query results
            Dim resultsDataTable As System.Data.DataTable = Nothing

            ' Declare a list of data columns.  This will hold the columns to be included in the query results
            Dim resultsDataColumnList As System.Collections.Generic.List(Of System.Data.DataColumn) = New System.Collections.Generic.List(Of System.Data.DataColumn)()

            ' Iterate through the columns in the graphics layer, adding each one to be included in
            ' the query results to the results data column list
            For Each dataColumn As System.Data.DataColumn In graphicsLayer.Columns
                ' If no fields were specified in the query; the current column name matches one of
                ' the specified field names; or the current column is a geometry column and geometries 
                ' are to be returned in the query, add a copy of the current column to the results column 
                ' list
                If adfQueryFilter.SubFields.Count = 0 OrElse adfQueryFilter.SubFields.IndexOf(dataColumn.ColumnName) <> -1 Then
                    resultsDataColumnList.Add(CloneColumn(dataColumn))
                ElseIf adfQueryFilter.ReturnADFGeometries AndAlso (dataColumn.DataType Is GetType(ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement) OrElse dataColumn.DataType Is GetType(ESRI.ArcGIS.ADF.Web.Geometry.Geometry)) Then
                    resultsDataColumnList.Add(CloneColumn(dataColumn))
                End If
            Next dataColumn

            ' If the query is to return geometries, initialize the results data table as a Web ADF graphics
            ' layer of the appropriate type
            If adfQueryFilter.ReturnADFGeometries Then
                If TypeOf graphicsLayer Is ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer Then
                    Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = TryCast(graphicsLayer, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)
                    resultsDataTable = New ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer(featureGraphicsLayer.TableName, resultsDataColumnList.ToArray(), featureGraphicsLayer.GeometryColumnName, featureGraphicsLayer.GraphicsIDColumn.ColumnName, featureGraphicsLayer.FeatureType)
                Else
                    Dim elementGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = TryCast(graphicsLayer, ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer)
                    resultsDataTable = New ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer(elementGraphicsLayer.TableName, resultsDataColumnList.ToArray(), elementGraphicsLayer.GraphicsColumn.ColumnName, elementGraphicsLayer.GraphicsIDColumn.ColumnName)
                End If
            End If

            ' If the query is not to return geometries, the results data table will not yet be intialized.
            ' Initialize it as an ordinary DataTable.
            If resultsDataTable Is Nothing Then
                resultsDataTable = New System.Data.DataTable(graphicsLayer.TableName)
                resultsDataTable.Columns.AddRange(resultsDataColumnList.ToArray())
            End If

            ' Execute the query using the appropriate private functions.  
            If TypeOf adfQueryFilter Is ESRI.ArcGIS.ADF.Web.SpatialFilter Then
                ' Get a reference to the passed-in query filter as a Web ADF Spatial Filter
                Dim adfSpatialFilter As ESRI.ArcGIS.ADF.Web.SpatialFilter = TryCast(adfQueryFilter, ESRI.ArcGIS.ADF.Web.SpatialFilter)

                If adfSpatialFilter.SearchOrder = ESRI.ArcGIS.ADF.Web.SearchOrder.Spatial Then
                    ' Execute the spatial query first, then filter the spatial query based on the
                    ' query filter's where clause
                    InitialSpatialQuery(graphicsLayer, resultsDataTable, adfSpatialFilter)
                    AttributeQueryOnSpatialResults(resultsDataTable, adfQueryFilter)
                Else
                    ' Execute the attribute query first, then filter the query results based on the filter
                    ' geometry.
                    Dim adfGeometryList As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geometry.Geometry) = InitialAttributeQuery(graphicsLayer, resultsDataTable, adfQueryFilter)
                    SpatialQueryOnAttributeResults(resultsDataTable, adfSpatialFilter, adfGeometryList)
                End If
            Else
                ' The query is non-spatial, so simply execute an attribute query based on the query filter's
                ' where clause
                InitialAttributeQuery(graphicsLayer, resultsDataTable, adfQueryFilter)
            End If

            ' If the results data table contains more records than the number specified by the query filter
            ' as the maximum, remove the extra results from the end of the table
            If resultsDataTable.Rows.Count > adfQueryFilter.MaxRecords AndAlso adfQueryFilter.MaxRecords > 0 Then
                Do While resultsDataTable.Rows.Count > adfQueryFilter.MaxRecords
                    resultsDataTable.Rows.RemoveAt(resultsDataTable.Rows.Count - 1)
                Loop
            End If

            Return resultsDataTable
        End Function

        ' Gets layers of the passed-in feature type that can be queried
        Public Sub GetQueryableLayers(ByVal mapFunctionalityName As String, <System.Runtime.InteropServices.Out()> ByRef layerIDs As String(), <System.Runtime.InteropServices.Out()> ByRef layerNames As String(), ByVal featureType As ESRI.ArcGIS.ADF.Web.FeatureType) Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.GetQueryableLayers
            layerIDs = Nothing
            layerNames = Nothing

            Dim queryableDataTableList As System.Collections.Generic.List(Of System.Data.DataTable) = New System.Collections.Generic.List(Of System.Data.DataTable)()

            ' Get the graphics dataset underlying the map functionality having the passed-in name
            Dim graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet = GetGraphicsDataSet(mapFunctionalityName)
            If graphicsDataSet Is Nothing Then
                Return
            End If

            ' Iterate through the graphics layers in the graphics dataset, adding layers that are 
            ' FeatureGraphicsLayers of the passed-in feature type to the data table list of queryable layers
            For Each graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer In graphicsDataSet.Tables
                Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = TryCast(graphicsLayer, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)
                ' If the layer is not a feature graphics layer or is not of the passed-in type, skip to the
                ' next layer
                If Not featureGraphicsLayer Is Nothing AndAlso featureGraphicsLayer.FeatureType <> featureType Then
                    Continue For
                End If

                queryableDataTableList.Add(graphicsLayer)
            Next graphicsLayer

            ' Re-dimension the output arrays with the number of queryable layers found
            layerIDs = New String(queryableDataTableList.Count - 1) {}
            layerNames = New String(queryableDataTableList.Count - 1) {}

            ' Iterate through the queryable table list, adding the name of each table to the output arrays
            Dim i As Integer = 0
            Do While i < queryableDataTableList.Count
                layerIDs(i) = queryableDataTableList(i).TableName
                layerNames(i) = queryableDataTableList(i).TableName
                i += 1
            Loop
        End Sub

        ' Gets all the layers referenced by the map functionality of the specified name that can be queried
        Public Sub GetQueryableLayers(ByVal mapFunctionalityName As String, <System.Runtime.InteropServices.Out()> ByRef layerIDs As String(), <System.Runtime.InteropServices.Out()> ByRef layerNames As String()) Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.GetQueryableLayers
            layerIDs = Nothing
            layerNames = Nothing

            ' Get the graphics dataset underlying the map functionality having the passed-in name
            Dim graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet = GetGraphicsDataSet(mapFunctionalityName)
            If graphicsDataSet Is Nothing Then
                Return
            End If

            ' Copy the names of the tables in the graphics dataset to the output arrays
            layerIDs = New String(graphicsDataSet.Tables.Count - 1) {}
            layerNames = New String(graphicsDataSet.Tables.Count - 1) {}
            Dim i As Integer = 0
            Do While i < graphicsDataSet.Tables.Count
                layerIDs(i) = graphicsDataSet.Tables(i).TableName
                layerNames(i) = graphicsDataSet.Tables(i).TableName
                i += 1
            Loop
        End Sub

        ' Gets the names of the fields in the layer specified by the passed-in ID
        Public Function GetFields(ByVal mapFunctionalityName As String, ByVal layerID As String) As String() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.GetFields
            ' Get the Web ADF graphics layer belonging to the map functionality specified by the passed-in
            ' name with the passed-in ID
            Dim graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = GetGraphicsLayer(mapFunctionalityName, layerID)
            If graphicsLayer Is Nothing Then
                Return Nothing
            End If

            ' Copy the names of all the columns in the graphics layer to a string array
            Dim fieldNameArray As String() = New String(graphicsLayer.Columns.Count - 1) {}
            Dim i As Integer = 0
            Do While i < graphicsLayer.Columns.Count
                fieldNameArray(i) = graphicsLayer.Columns(i).ColumnName
                i += 1
            Loop

            Return fieldNameArray
        End Function

        ' Gets the names and types of the fields in the layer specified by the passed-in ID
        Public Function GetFields(ByVal mapFunctionalityName As String, ByVal layerID As String, <System.Runtime.InteropServices.Out()> ByRef fieldTypes As System.Type()) As String() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.GetFields
            ' Get the Web ADF graphics layer belonging to the map functionality specified by the passed-in
            ' name with the passed-in ID
            Dim graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = GetGraphicsLayer(mapFunctionalityName, layerID)
            If graphicsLayer Is Nothing Then
                fieldTypes = Nothing
                Return Nothing
            End If

            ' Copy the names of all the columns in the graphics layer to a string array, and the types of
            ' the columns to the passed-in type array
            Dim fieldNameArray As String() = New String(graphicsLayer.Columns.Count - 1) {}
            fieldTypes = New System.Type(graphicsLayer.Columns.Count - 1) {}
            Dim i As Integer = 0
            Do While i < graphicsLayer.Columns.Count
                fieldNameArray(i) = graphicsLayer.Columns(i).ColumnName
                fieldTypes(i) = graphicsLayer.Columns(i).DataType
                i += 1
            Loop

            Return fieldNameArray
        End Function

#End Region

#Region "IGISFunctionality implementation"

        Public Property WebControl() As System.Web.UI.WebControls.WebControl Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.WebControl
            Get
                Return m_webControl
            End Get
            Set(ByVal value As System.Web.UI.WebControls.WebControl)
                m_webControl = Value
            End Set
        End Property

        Public Property Name() As String Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Name
            Get
                Return m_name
            End Get
            Set(ByVal value As String)
                m_name = Value
            End Set
        End Property

        Public Property Resource() As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Resource
            Get
                Return m_gisResource
            End Get
            Set(ByVal value As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource)
                m_gisResource = Value
            End Set
        End Property

        Public ReadOnly Property Initialized() As Boolean Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Initialized
            Get
                Return m_initialized
            End Get
        End Property

        Public Sub LoadState() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.LoadState
        End Sub

        Public Sub Initialize() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Initialize
            m_initialized = True
        End Sub

        Public Sub SaveState() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.SaveState
        End Sub

        ' Set the flag indicating whether the functionality is intitialized to false.  Any necessary
        ' disposal logic (e.g. releasing object references) should go here.  Note that, if there is
        ' additional logic here, users of this class will have to EXPLCITLY call dispose.  It is not
        ' invoked by other Web ADF components or the Page life-cycle.
        Public Sub Dispose() Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Dispose
            m_initialized = False
        End Sub

        Public Function Supports(ByVal operation As String) As Boolean Implements ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality.Supports
            Return True
        End Function

#End Region
    End Class
End Namespace