Common Custom controls
Common_CustomControls_VBNet\ADFWebPart\MapGridViewWebPart.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 ADFWebPart_VBNet
    ' Comments following "///" are recognized as XML documentation comments.  Different documentation
    ' comment types can be specified by using different tags.  Some comment types, such as <summary>
    ' and <param>, show up in Intellisense, while others, such as <remarks> show up in the Object
    ' Browser.  XML documentation files can be generated from classes containing such documentation.

    ''' <summary>
    ''' WebPart containing an ArcGIS Server Web ADF Map Control with a linked GridView
    ''' </summary>
    ''' <remarks>
    ''' The web part's grid view displays data in the current map extent for a user-specified 
    ''' layer in a user-specified resource.  Hovering over a grid view row highlights the 
    ''' corresponding map feature, and clicking on the row zooms to the feature.
    ''' 
    ''' <para>Note that, for the map to initialize properly, a valid ArcGIS Server geometry
    ''' service URL must be specified, either via the constructor or the GeometryServiceUrl
    ''' property.</para>
    ''' </remarks>
    <System.Web.UI.ToolboxData("<{0}:MapGridViewWebPart runat=""server""></{0}:MapGridViewWebPart>"), System.ComponentModel.Designer(GetType(ADFWebPart_VBNet.MapGridViewWebPartDesigner), GetType(System.ComponentModel.Design.IDesigner))> _
    Public Class MapGridViewWebPart
        Inherits AJAXSharePointWebPart
#Region "Instance Variable Declarations"

        Private m_adfMap As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map = Nothing
        Private m_mapResourceManager As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager = Nothing
        Private m_updatePanel As System.Web.UI.UpdatePanel = Nothing
        Private m_graphicsDataLayerName As String = Nothing
        Private m_dataLayerGridView As System.Web.UI.WebControls.GridView = Nothing
        Private m_gridViewDataBound As Boolean = False
        Private m_hasScriptManager As Boolean = False
        Private m_graphicsMapResourceID As String
        Private m_dataLayerMapResourceID As String
        Private m_backgroundMapResourceID As String


        Private m_dataSourceType As String
        Private m_userDataSource As String
        Private m_mapResourceDefinition As String
        Private m_dataLayerName As String
        Private m_geometryServiceUrl As String
        Private m_maxFeatures As Integer
        Private m_fieldsCollection As System.Collections.Specialized.NameValueCollection

#End Region

#Region "Constructors"

        ''' <summary>
        ''' Creates a new, uninitialized instance of the MapGridViewWebPart class.
        ''' </summary>
        Public Sub New()
            ' Default property values
            m_dataSourceType = "ArcGIS Server Internet"
            m_userDataSource = "http://serverapps.esri.com/arcgis/services/SamplesNET"
            ' SanFrancisco.mxd in <ArcGIS Developer Kit Install>\Samples\data\SanFrancisco
            m_mapResourceDefinition = "1@Layers@SanFrancisco"
            m_dataLayerName = "customers"
            m_graphicsDataLayerName = m_dataLayerName & "_Graphics"
            m_geometryServiceUrl = "http://serverapps.esri.com/arcgis/services/Geometry/GeometryServer"
            m_maxFeatures = 20

            ' Initialize a NameValueCollection to specify how we want fields to be displayed.  This specifies the
            ' field display for both the WebPart's GridView and callouts when features are hovered over.
            m_fieldsCollection = New System.Collections.Specialized.NameValueCollection()
            m_fieldsCollection.Add("NAME", "Site Name")
            m_fieldsCollection.Add("ADDRESS", "Address")
            'm_fieldsCollection.Add("SALES", "Sales");
        End Sub

        ''' <summary>
        ''' Instantiates a fully parameterized MapGridViewWebPart
        ''' </summary>
        ''' <param name="dataSource">Machine name or URL that specifies the host of the ArcGIS
        ''' server service</param>
        ''' <param name="resourceDefinition">Service to use in the format 
        ''' &lt;DataFrameName&gt;@&lt;ServiceName&gt;</param>
        ''' <param name="resourceType">"ArcGIS Server Local" or "ArcGIS Server Internet"</param>
        ''' <param name="dataLayerName">Layer for which data will be displayed in the GridView</param>
        ''' <param name="geometryServiceUrl">URL of an ArcGIS Server Geometry Service</param>
        Public Sub New(ByVal dataSource As String, ByVal resourceDefinition As String, ByVal resourceType As String, ByVal dataLayerName As String, ByVal geometryServiceUrl As String)
            m_userDataSource = dataSource
            m_mapResourceDefinition = resourceDefinition
            m_dataSourceType = resourceType
            m_dataLayerName = dataLayerName
            m_graphicsDataLayerName = m_dataLayerName & "_Graphics"
            m_geometryServiceUrl = geometryServiceUrl
        End Sub
#End Region

#Region "WebControl Life Cycle Event Handlers"

        Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
            MyBase.OnInit(e)

            m_graphicsMapResourceID = "GraphicsMapResource<!--" & Me.UniqueID & "-->"
            m_dataLayerMapResourceID = "AGSMapResource<!--" & Me.UniqueID & "-->"
            m_backgroundMapResourceID = "AGSBackground<!--" & Me.UniqueID & "-->"

            'Make sure there is a script manager on the page
            If Not System.Web.UI.ScriptManager.GetCurrent(Me.Page) Is Nothing Then
                m_hasScriptManager = True

            End If
        End Sub

        ' Handles custom postback row click event, if enabled
        Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
            MyBase.OnLoad(e)

            ' Make sure the page has a script manager
            If propertyCheck() Then
                ' Get the __EVENTTARGET request parameter.  This will specify any controls that are
                ' the target of the event that triggered the Page request (i.e. postback)
                Dim requestParameters As System.Collections.Specialized.NameValueCollection = Page.Request.Params
                Dim controlEvent As String = Nothing
                Dim controlID As String = requestParameters("__EVENTTARGET")

                ' Make sure the event target is not null or empty and that it contains the ID of
                ' the web part's GridView

                ' Ensure child controls are created before checked
                EnsureChildControls()

                If (Not String.IsNullOrEmpty(controlID)) AndAlso controlID.Contains(m_dataLayerGridView.ID) Then
                    ' Get any event arguments from the Page request
                    controlEvent = requestParameters("__EVENTARGUMENT")
                    ' If the event arguments contain the string indicating initiation of our
                    ' custom postback event, call the method to select the clicked row
                    If controlEvent.Contains("SelectClickedRow$") Then
                        SelectRow(controlEvent)
                    End If
                End If
            End If
        End Sub

        ' Creates the controls contained in the WebPart - A Map, GridView, and UpdatePanel
        Protected Overrides Sub CreateChildControls()
            Try
                MyBase.CreateChildControls()
                ' Make sure the page has a ScriptManager.  This is required for managing the 
                ' asynchronous Map-GridView interaction via an UpdatePanel
                If propertyCheck() Then
                    ' Create the MapResourceManager that will be used by the WebPart's Map
                    m_mapResourceManager = New ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager()
                    m_mapResourceManager.ID = "webPartMapResourceManager"
                    Controls.Add(m_mapResourceManager)

                    ' Create the WebPart's Map Control
                    m_adfMap = New ESRI.ArcGIS.ADF.Web.UI.WebControls.Map()
                    m_adfMap.ID = Me.ID & "_webPartMap"
                    m_adfMap.Visible = True
                    m_adfMap.Height = 300
                    m_adfMap.Width = 300
                    m_adfMap.Style.Add("position", "relative")
                    m_adfMap.Style.Add("top", "0px")
                    m_adfMap.Style.Add("left", "0px")
                    m_adfMap.MapResourceManager = m_mapResourceManager.UniqueID
                    AddHandler m_adfMap.ExtentChanged, AddressOf Map_ExtentChanged

                    ' Create an HTML table to hold the Map and GridView
                    Dim table As System.Web.UI.WebControls.Table = New System.Web.UI.WebControls.Table()
                    table.Width = New System.Web.UI.WebControls.Unit(100, System.Web.UI.WebControls.UnitType.Percentage)

                    ' Add a row to the table
                    Dim tableRow As System.Web.UI.WebControls.TableRow
                    tableRow = New System.Web.UI.WebControls.TableRow()
                    table.Rows.Add(tableRow)

                    ' Add a cell to the table and put the Map in it
                    Dim tableCell As System.Web.UI.WebControls.TableCell
                    tableCell = New System.Web.UI.WebControls.TableCell()
                    tableCell.Style(System.Web.UI.HtmlTextWriterStyle.VerticalAlign) = "top"
                    tableRow.Cells.Add(tableCell)
                    tableCell.Controls.Add(m_adfMap)

                    ' Create the GridView
                    m_dataLayerGridView = CreateGridView()

                    ' Create an update panel to hold the GridView
                    m_updatePanel = New System.Web.UI.UpdatePanel()
                    m_updatePanel.ID = Me.ID & "_updatePanel"
                    m_updatePanel.ChildrenAsTriggers = True
                    m_updatePanel.UpdateMode = System.Web.UI.UpdatePanelUpdateMode.Conditional

                    ' Create a trigger to wire the GridView to the map's extent changed event
                    Dim mapTrigger As System.Web.UI.AsyncPostBackTrigger = New System.Web.UI.AsyncPostBackTrigger()
                    ' Set the trigger control to be the Map
                    mapTrigger.ControlID = Me.ID & "_webPartMap"
                    ' Set the trigger event to ExtentChanged
                    mapTrigger.EventName = "ExtentChanged"
                    ' Add the trigger to the update panel
                    m_updatePanel.Triggers.Add(mapTrigger)

                    ' Add the GridView to the UpdatePanel
                    m_updatePanel.ContentTemplateContainer.Controls.Add(m_dataLayerGridView)

                    ' Create another table cell, add it to the current table row, and put the
                    ' UpdatePanel containing the GridView in it
                    tableCell = New System.Web.UI.WebControls.TableCell()
                    tableCell.Style(System.Web.UI.HtmlTextWriterStyle.VerticalAlign) = "top"
                    tableRow.Cells.Add(tableCell)
                    tableCell.Controls.Add(m_updatePanel)

                    Controls.Add(table)
                Else
                    ' Create a label stating that this web part requires a ScriptManager.  A more
                    ' robust implemenation could include web part configuration with the Callback
                    ' Framework if a ScriptManager is unavailable.
                    If (Not m_hasScriptManager) Then
                        Dim noScriptManagerLabel As System.Web.UI.WebControls.Label = New System.Web.UI.WebControls.Label()
                        noScriptManagerLabel.ID = "lblNoScriptManager"
                        noScriptManagerLabel.Text = "A Script Manager must exist on the page for this web " & "part to be used."
                        Controls.Add(noScriptManagerLabel)
                    Else
                        Dim noResourceDefLabel As System.Web.UI.WebControls.Label = New System.Web.UI.WebControls.Label()
                        noResourceDefLabel.ID = "lblNoResourceDef"
                        noResourceDefLabel.Text = "Data source type and definition, resource definition, and data layer name must be defined."
                        Controls.Add(noResourceDefLabel)
                    End If
                End If
            Catch ex As System.Exception
                System.Diagnostics.Debug.Write(ex.Message)
            End Try
        End Sub

        ' Executes before the control is rendered
        Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
            MyBase.OnPreRender(e)

            If propertyCheck() Then
                ' Add resources to the map resource manager
                Dim mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = AddResourcesToMapResourceManager()

                ' Initialize the map's extent based on the user-specified data layer
                If Not m_mapResourceManager Is Nothing AndAlso Not mapResourceItem Is Nothing Then
                    InitializeMapExtent(mapResourceItem)
                End If

                ' Check whether the WebPart user has specified fields for display
                m_dataLayerGridView.Columns.Clear()
                If Not m_fieldsCollection Is Nothing Then
                    ' Create BoundFields for each of the fields specified by the user and add them to the GridView
                    Dim i As Integer = 0
                    Do While i < m_fieldsCollection.Count
                        Dim boundField As System.Web.UI.WebControls.BoundField = New System.Web.UI.WebControls.BoundField()
                        boundField.DataField = m_fieldsCollection.GetKey(i)
                        boundField.SortExpression = m_fieldsCollection.GetKey(i)
                        boundField.HeaderText = m_fieldsCollection(i)
                        m_dataLayerGridView.Columns.Add(boundField)
                        i += 1
                    Loop

                    ' Since we have explicitly specified fields for display, we do not want the GridView to
                    ' generate its own fields
                    m_dataLayerGridView.AutoGenerateColumns = False
                Else
                    ' Since the user has not specified fields, we specify that the GridView should generate 
                    ' columns automatically based on the data to which it is bound
                    m_dataLayerGridView.AutoGenerateColumns = True
                End If

                ' Get the functionality for the graphics resource
                Dim graphicsMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality = CType(m_adfMap.GetFunctionality(m_graphicsMapResourceID), ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality)

                ' If the graphics resource exists and already contains a table with the graphics data layer 
                ' name then we don't want to execute the code to create the graphics layer
                If (Not graphicsMapFunctionality Is Nothing) AndAlso (Not graphicsMapFunctionality.GraphicsDataSet.Tables.Contains(m_graphicsDataLayerName)) Then
                    ' Get the total number of features in the data layer by passing in a null
                    ' extent to GetDataLayerFeaturesInExtent and temporarily setting the 
                    ' member variable specifying the maximum number of features to the 
                    ' maximum for the integer data type.
                    Dim maxFeatures As Integer = m_maxFeatures
                    m_maxFeatures = Integer.MaxValue
                    Dim resultsDataTable As System.Data.DataTable = GetDataLayerFeaturesInExtent(Nothing)
                    m_adfMap.Page.Session("totalDataLayerFeatures") = resultsDataTable.Rows.Count
                    m_maxFeatures = maxFeatures

                    ' Initialize the graphics data layer.  If the total number of features in the
                    ' user-specified data layer is less than the maximum features property, then
                    ' all of the data layer features can be included in the graphics layer.  We 
                    ' therefore pass in a null extent to GetDataLayerFeaturesInExtent so that all 
                    ' the features in the data layer are included in the data table on which the
                    ' graphics layer is based.  In this case, the graphics layer is only created
                    ' here.  If the total number of features is greater than the maximum features
                    ' property, then we limit the features in the data table to those in the 
                    ' initial map extent.  In this case, since not all of the features can be
                    ' included in the graphics layer at once, the graphics layer will be recreated
                    ' every time the map extent is changed.
                    If resultsDataTable.Rows.Count <= m_maxFeatures Then
                        resultsDataTable = GetDataLayerFeaturesInExtent(Nothing)
                    Else
                        resultsDataTable = GetDataLayerFeaturesInExtent(m_adfMap.Extent)
                    End If

                    CreateGraphicsDataLayer(resultsDataTable)
                    m_dataLayerGridView.DataSource = resultsDataTable
                    m_dataLayerGridView.DataBind()
                    ' If a user navigates away from the page and returns during the same session, the GridView needs to bind to 
                    ' the data again.   The m_gridViewDataBound variable tracks if the GridView has bound to the data.  
                ElseIf (Not m_gridViewDataBound) Then
                    Try
                        ' Get the data layer features in the new extent and update the graphics data
                        ' layer accordingly
                        Dim queryResultsDataTable As System.Data.DataTable = GetDataLayerFeaturesInExtent(m_adfMap.Extent)

                        ' If the total number of features in the data layer is greater than the max features property, 
                        ' then we need to recreate the data graphics layer.  Otherwise, the records in 
                        ' queryResultsDataTable (which will be displayed on the WebPart's GridView) may include records 
                        ' that are not in the graphics layer
                        If (Not m_adfMap.Page.Session("totalDataLayerFeatures") Is Nothing) AndAlso (CInt(Fix(m_adfMap.Page.Session("totalDataLayerFeatures"))) > m_maxFeatures) Then
                            CreateGraphicsDataLayer(queryResultsDataTable)
                        End If

                        ' Store the results table in session for use when the GridView is paging
                        Dim resultsTableSessionKey As String = String.Format("{0}_queryResultsDataTable", Me.ID)
                        Page.Session(resultsTableSessionKey) = queryResultsDataTable

                        ' Initialize the data source of the WebPart's GridView with the query results
                        m_dataLayerGridView.DataSource = queryResultsDataTable

                        ' Turn on paging
                        m_dataLayerGridView.PageSize = 5
                        m_dataLayerGridView.AllowPaging = True
                        m_dataLayerGridView.PagerTemplate = Nothing

                        ' Bind the data source to the GridView
                        m_dataLayerGridView.DataBind()
                    Catch ex As System.Exception
                        System.Diagnostics.Debug.WriteLine(ex.Message)
                    End Try
                End If
            End If
        End Sub

        ' Renders the web control on the page
        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            ' Check whether the control is being rendered at design-time or run-time
            If DesignMode Then
                ' Since the control is being rendered at design-time, we will generate a label
                ' showing the control's ID, the control's type, and, if no ScriptManager is
                ' present, a warning that one is required

                ' Initialize a label with the control ID
                Dim IDLabel As System.Web.UI.WebControls.Label = New System.Web.UI.WebControls.Label()
                IDLabel.Font.Name = "Verdana"
                IDLabel.Font.Size = 9
                IDLabel.Text = "&nbsp;" & Me.ID & "<br /><br />"
                ' Render the label
                IDLabel.RenderControl(writer)


                ' Initialize a label with the control type
                Dim typeLabel As System.Web.UI.WebControls.Label = New System.Web.UI.WebControls.Label()
                typeLabel.Font.Name = "Verdana"
                typeLabel.Font.Size = 9
                typeLabel.Text = "&nbsp;MapGridViewWebPart<br /><br />"
                ' Render the label
                typeLabel.RenderControl(writer)

                ' Check whether the Page contains a script manager
                Dim hasScriptManager As Boolean = HasControlOfType(GetType(System.Web.UI.ScriptManager), Page.Controls)
                If (Not hasScriptManager) Then
                    ' Format warning labels

                    ' Create a label in red, bold font for the warning title
                    Dim WarningTitleLabel As System.Web.UI.WebControls.Label = New System.Web.UI.WebControls.Label()
                    WarningTitleLabel.Font.Name = "Verdana"
                    WarningTitleLabel.Font.Size = 8
                    WarningTitleLabel.Font.Bold = True
                    WarningTitleLabel.ForeColor = System.Drawing.Color.Red
                    WarningTitleLabel.Text = "&nbsp;Warning: "
                    ' Render the warning title
                    WarningTitleLabel.RenderControl(writer)

                    ' Create a label with the description of the problem
                    Dim WarningDescriptionLabel As System.Web.UI.WebControls.Label = New System.Web.UI.WebControls.Label()
                    WarningDescriptionLabel.Font.Name = "Verdana"
                    WarningDescriptionLabel.Font.Size = 8
                    WarningDescriptionLabel.Text = "This control requires that<br />&nbsp;" & "a ScriptManager be on the Page"
                    ' Render the warning description
                    WarningDescriptionLabel.RenderControl(writer)
                End If

            Else
                MyBase.RenderContents(writer)
            End If
        End Sub

#End Region

#Region "Child Control Event Handlers"

        ' Fires when the extent of the WebPart's Map Control changes
        Private Sub Map_ExtentChanged(ByVal sender As Object, ByVal extentEventArgs As ESRI.ArcGIS.ADF.Web.UI.WebControls.ExtentEventArgs)
            ' Make sure the event is not firing on map load (OldExtent == null) and the old and
            ' new extents are distinct
            If Not extentEventArgs.OldExtent Is Nothing AndAlso (Not extentEventArgs.OldExtent.Equals(extentEventArgs.NewExtent)) Then
                Try
                    ' Get the data layer features in the new extent and update the graphics data
                    ' layer accordingly
                    Dim queryResultsDataTable As System.Data.DataTable = GetDataLayerFeaturesInExtent(extentEventArgs.NewExtent)

                    ' If the total number of features in the data layer is greater than the max features property, 
                    ' then we need to recreate the data graphics layer.  Otherwise, the records in 
                    ' queryResultsDataTable (which will be displayed on the WebPart's GridView) may include records 
                    ' that are not in the graphics layer
                    If (Not m_adfMap.Page.Session("totalDataLayerFeatures") Is Nothing) AndAlso (CInt(Fix(m_adfMap.Page.Session("totalDataLayerFeatures"))) > m_maxFeatures) Then
                        CreateGraphicsDataLayer(queryResultsDataTable)
                    End If

                    ' Store the results table in session for use when the GridView is paging
                    Dim resultsTableSessionKey As String = String.Format("{0}_queryResultsDataTable", Me.ID)
                    Page.Session(resultsTableSessionKey) = queryResultsDataTable

                    ' Initialize the data source of the WebPart's GridView with the query results
                    m_dataLayerGridView.DataSource = queryResultsDataTable

                    ' Turn on paging
                    m_dataLayerGridView.PageSize = 5
                    m_dataLayerGridView.AllowPaging = True
                    m_dataLayerGridView.PagerTemplate = Nothing

                    ' Bind the data source to the GridView
                    m_dataLayerGridView.DataBind()
                Catch ex As System.Exception
                    System.Diagnostics.Debug.WriteLine(ex.Message)
                End Try
            End If
        End Sub

        ' Adds interaction with the Map to each GridView row's mouseOver and click events
        Private Sub GridView_RowDataBound(ByVal sender As Object, ByVal gridViewRowEventArgs As System.Web.UI.WebControls.GridViewRowEventArgs)
            ' Make sure the row is a valid DataRow
            If gridViewRowEventArgs.Row.RowType = System.Web.UI.WebControls.DataControlRowType.DataRow Then
                ' Get the graphics resource from the map
                Dim graphicsMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality = CType(m_adfMap.GetFunctionality(m_graphicsMapResourceID), ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality)

                ' Make sure the graphics resource contains a layer named "DataLayer."  This is where
                ' the feature graphics for the data layer features in the current extent are stored.
                If graphicsMapFunctionality.GraphicsDataSet.Tables.Contains(m_graphicsDataLayerName) Then
                    ' Get the current row
                    Dim gridViewRow As System.Web.UI.WebControls.GridViewRow = gridViewRowEventArgs.Row

                    ' Get a reference to the DataLayer as a Web ADF GraphicsLayer
                    Dim adfGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = TryCast(graphicsMapFunctionality.GraphicsDataSet.Tables(m_graphicsDataLayerName), ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)

                    ' Get the client-side ID of the graphics layer 
                    Dim dataLayerID As String = m_adfMap.GetGraphicsLayerClientID(adfGraphicsLayer)

                    ' Find the index of the first unique ID column in the graphics layer's table
                    Dim uniqueIDindex As Integer = -1
                    Dim i As Integer = 0
                    Do While i < adfGraphicsLayer.Columns.Count
                        If adfGraphicsLayer.Columns(i).Unique Then
                            uniqueIDindex = i
                            Exit Do
                        End If
                        i += 1
                    Loop

                    ' Get the index of the row in the graphics layer's table that has a unique ID
                    ' matching that of the current row
                    Dim dataRowView As System.Data.DataRowView = TryCast(gridViewRow.DataItem, System.Data.DataRowView)
                    Dim graphicFeatureIndex As Integer = -1
                    i = 0
                    Do While i < adfGraphicsLayer.Rows.Count
                        If adfGraphicsLayer.Rows(i)(uniqueIDindex).Equals(dataRowView(uniqueIDindex)) Then
                            graphicFeatureIndex = i
                            Exit Do
                        End If
                        i += 1
                    Loop

                    ' Create a JavaScript string that will (1) set the background color of the current
                    ' row, (2) get the graphicFeatureGroup (i.e. layer) for the DataLayer, (3) get
                    ' the graphicFeature corresponding to the current row index from the 
                    ' graphicFeatureGroup, and (4) set the highlight on the graphicFeature
                    Dim jsSetRowAndFeatureHighlight As String = "try {{" & "this.style.backgroundColor='{0}';" & "var graphicFeatureGroup = $find('{1}'); " & "var graphicFeature = graphicFeatureGroup.get({2}); " & "graphicFeature.set_highlight({3});" & "}} catch(ex) {{}}"

                    ' Assign the JavaScript string to the current row's onmouseover event.  Specify the
                    ' row's background color as gray and the parameter to graphicFeature.set_highlight
                    ' as true
                    gridViewRow.Attributes.Add("onmouseover", String.Format(jsSetRowAndFeatureHighlight, "yellow", dataLayerID, graphicFeatureIndex, "true"))

                    ' Assign the JavaScript string to the current row's onmouseout event.  Specify the
                    ' row's background color as white and the parameter to graphicFeature.set_highlight
                    ' as false
                    gridViewRow.Attributes.Add("onmouseout", String.Format(jsSetRowAndFeatureHighlight, "white", dataLayerID, graphicFeatureIndex, "false"))

                    ' Uncomment the following code block to add a custom postback event that fires 
                    ' when a row is clicked

                    '// Get the JavaScript that will initiate a postback event with the parameters
                    '// specified
                    'string serverClick = this.Page.ClientScript.GetPostBackEventReference(
                    '    gridViewRow, "SelectClickedRow$" + gridViewRow.RowIndex);
                    '// Add the postback JavaScript to the row's onclick event
                    'gridViewRow.Attributes.Add("onclick", serverClick);

                    ' Construct a JavaScript string that uses Web ADF JavaScript to (1) retrieve the
                    ' graphicFeatureGroup (i.e. layer) corresponding to the graphics data layer, (2)
                    ' get the graphicFeature corresponding to the current row, (3) get the geometry of
                    ' the graphicFeature, (4) get the envelope of the geometry, (5) get the WebPart's
                    ' client-side map object, and (6) zoom to the graphicFeature's envelope
                    Dim jsZoomToFeature As String = String.Format("try {{" & "var graphicFeatureGroup = $find('{0}'); " & "var graphicFeature = graphicFeatureGroup.get({1}); " & "var geometry = graphicFeature.get_geometry();" & "var envelope = geometry.getEnvelope();" & "var map = $find('{2}');" & "map.zoomToBox(envelope, true);" & "}} catch(ex) {{}}", dataLayerID, graphicFeatureIndex, m_adfMap.ClientID)
                    ' Add the JavaScript to the current row's onclick event
                    gridViewRow.Attributes.Add("onclick", jsZoomToFeature)
                End If
            End If
        End Sub

        ' Fires when the Page is changed in the GridView
        Private Sub GridView_PageIndexChanging(ByVal sender As Object, ByVal gridViewPageEventArgs As System.Web.UI.WebControls.GridViewPageEventArgs)
            ' Get a reference to the GridView
            Dim gridView As System.Web.UI.WebControls.GridView = TryCast(sender, System.Web.UI.WebControls.GridView)

            ' Get the page that is being paged to
            gridView.PageIndex = gridViewPageEventArgs.NewPageIndex

            ' Get the data table storing the features in the current extent from session
            Dim resultsTableSessionKey As String = String.Format("{0}_queryResultsDataTable", Me.ID)
            Dim resultsDataTable As System.Data.DataTable = TryCast(Page.Session(resultsTableSessionKey), System.Data.DataTable)
            gridView.DataSource = resultsDataTable
            gridView.DataBind()
        End Sub

#End Region

#Region "Child Control Initialization Methods"

        ' Creates and initializes a GridView for use in the WebPart
        Private Function CreateGridView() As System.Web.UI.WebControls.GridView
            ' Create the GridView and set dimensional/positional attributes
            Dim gridView As System.Web.UI.WebControls.GridView = New System.Web.UI.WebControls.GridView()
            gridView = New System.Web.UI.WebControls.GridView()
            gridView.ID = Me.ID & "_gridView"
            gridView.Width = New System.Web.UI.WebControls.Unit(350, System.Web.UI.WebControls.UnitType.Pixel)
            gridView.Style("position") = "relative"
            gridView.Style("top") = "0px"
            gridView.Style("left") = "0px"
            gridView.Style("width") = "350px"



            ' Add an event handler for the GridView's RowDataBound event.  This handler will add
            ' functionality to each row's onmouseover and onclick events.
            AddHandler gridView.RowDataBound, AddressOf GridView_RowDataBound

            AddHandler gridView.DataBound, AddressOf GridView_DataBound

            ' Add an event handler for the GridView's PageIndexChanging event.  This handler will
            ' re-bind the GridView to the DataLayer's currently visible features so they are 
            ' displayed properly on the new page.
            AddHandler gridView.PageIndexChanging, AddressOf GridView_PageIndexChanging

            Return gridView
        End Function

        Private Sub GridView_DataBound(ByVal sender As Object, ByVal e As System.EventArgs)
            m_gridViewDataBound = True
        End Sub

        ' Adds map resources to the map resource manager
        Private Function AddResourcesToMapResourceManager() As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem
            ' Check whether a data source was specified by the user.  Optionally, initialize with defaults. 
            'if (string.IsNullOrEmpty(m_userDataSource))
            '{
            '    m_mapResourceType = "ArcGIS Server Internet";
            '    m_userDataSource = "http://localhost/arcgis/services";
            '    m_mapResourceDefinition = "1@Layers@SanFrancisco";
            '    m_dataLayerName = "customers";
            '}

            ' Declare resource item variables
            Dim mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = Nothing
            Dim backgroundResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = Nothing

            ' Make sure the map resource manager exists and that it does not contain any resource
            ' items before initializing
            If Not m_mapResourceManager Is Nothing AndAlso m_mapResourceManager.ResourceItems.Count = 0 Then
                ' Create the background resource item and initialize it to display the 
                ' ESRI_StreetMap_World_2D ArcGIS Online service
                Dim gisBackgroundItemDefinition As ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition = Utility.CreateGISResourceItemDefinition("http://server.arcgisonline.com/v93", "ArcGIS Server Internet", String.Empty, "(default)@ESRI_StreetMap_World_2D", True)
                backgroundResourceItem = Utility.CreateResourceItem(m_backgroundMapResourceID, gisBackgroundItemDefinition)
                backgroundResourceItem.Parent = m_mapResourceManager
                backgroundResourceItem.InitializeResource()

                ' Add the background resource item to the map resource manager
                Utility.AddMapResourceItemToResourceManager(m_mapResourceManager, False, backgroundResourceItem)

                ' Create the resource item and initialize it to display the user-specified resource
                Dim gisResourceItemDefinition As ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition = Utility.CreateGISResourceItemDefinition(m_userDataSource, m_dataSourceType, String.Empty, m_mapResourceDefinition, True)
                mapResourceItem = Utility.CreateResourceItem(m_dataLayerMapResourceID, gisResourceItemDefinition)
                mapResourceItem.Parent = m_mapResourceManager
                mapResourceItem.InitializeResource()

                ' Add the user-specified resource item to the map resource manager
                Utility.AddMapResourceItemToResourceManager(m_mapResourceManager, True, mapResourceItem)

                ' Create a graphics resource to display graphic features for the user-specified
                ' data layer
                Dim graphicsDataSourceDefinition As String = "In Memory"
                Dim graphicsDataSourceType As String = "GraphicsLayer"

                Dim graphicsResourceItemDefinition As ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition = Utility.CreateGISResourceItemDefinition(graphicsDataSourceDefinition, graphicsDataSourceType, "", "", True)
                Dim graphicsResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = Utility.CreateResourceItem(m_graphicsMapResourceID, graphicsResourceItemDefinition)
                graphicsResourceItem.Parent = m_mapResourceManager
                graphicsResourceItem.InitializeResource()

                ' Add the graphics resource item to the map resource manager
                Utility.AddMapResourceItemToResourceManager(m_mapResourceManager, True, graphicsResourceItem)
            ElseIf m_mapResourceManager.ResourceItems.Count > 0 Then
                Dim gisResourceItemDefinition As ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition = Utility.CreateGISResourceItemDefinition(m_userDataSource, m_dataSourceType, String.Empty, m_mapResourceDefinition, True)

                Dim existingMapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = m_mapResourceManager.ResourceItems.Find(m_dataLayerMapResourceID)
                Dim existingResourceItemDefinition As String = existingMapResourceItem.Definition.ToString()

                ' Check to see if a new resource definition was provided by the user, if so, switch it out.
                If existingResourceItemDefinition = gisResourceItemDefinition.ToString() Then
                    Return mapResourceItem
                End If

                m_mapResourceManager.ResourceItems.Remove(existingMapResourceItem)

                mapResourceItem = Utility.CreateResourceItem(m_dataLayerMapResourceID, gisResourceItemDefinition)
                mapResourceItem.Parent = m_mapResourceManager
                mapResourceItem.InitializeResource()

                ' Add the user-specified resource item to the map resource manager
                m_mapResourceManager.ResourceItems.Insert(1, mapResourceItem)
            End If

            Return mapResourceItem
        End Function

        ' Initializes the map extent to the default extent of the passed-in map resource item
        Private Sub InitializeMapExtent(ByVal mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem)
            ' Get the resource from the passed-in resource item
            Dim commonMapResource As ESRI.ArcGIS.ADF.Web.DataSources.IMapResource = TryCast(mapResourceItem.Resource, ESRI.ArcGIS.ADF.Web.DataSources.IMapResource)

            If Not commonMapResource Is Nothing Then
                Try
                    ' Get ArcGIS Server data source specific spatial references from the common 
                    ' ADF spatial references of the passed-in resource and the WebPart's map                    
                    Dim agsResourceSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.FromAdfSpatialReference(commonMapResource.MapInformation.DefaultSpatialReference)
                    Dim agsMapSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.FromAdfSpatialReference(m_adfMap.SpatialReference)

                    ' Get an ArcGIS Server data-source specific envelope from the default extent of
                    ' the passed-in resource
                    Dim agsResourceExtent As ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.FromAdfEnvelope(commonMapResource.MapInformation.DefaultExtent)

                    ' Initialize an array of ArcGIS Server data-source specific geometries with
                    ' the converted resource extent
                    Dim agsInGeometryArray As ESRI.ArcGIS.ADF.ArcGISServer.Geometry()
                    agsInGeometryArray = New ESRI.ArcGIS.ADF.ArcGISServer.Geometry(0) {}
                    agsInGeometryArray(0) = agsResourceExtent

                    ' Get a reference to the geometry service specified by the geometry service
                    ' URL property
                    Dim geometryServer As ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy = New ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy(m_geometryServiceUrl)

                    ' Use the project method of the Geometry Service to project the extent of the
                    ' passed-in resource to the spatial reference of the map
                    Dim agsOutGeometryArray As ESRI.ArcGIS.ADF.ArcGISServer.Geometry() = geometryServer.Project(agsResourceSpatialReference, agsMapSpatialReference, False, Nothing, Nothing, agsInGeometryArray)

                    ' Get the projected extent from the output geometry array
                    agsResourceExtent = TryCast(agsOutGeometryArray(0), ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN)

                    ' Convert the projected extent back to the ADF common envelope type and pass it to
                    ' the map's extent property
                    m_adfMap.Extent = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.ToAdfEnvelope(agsResourceExtent)
                Catch
                    ' Extent initialization failed, so initialize with a default extent
                    m_adfMap.Extent = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(-122.42883176338381, 37.78331593769353, -122.41595716011233, 37.790826122935229)
                End Try
            End If
        End Sub

#End Region

#Region "Other Instance Methods"

        ' Method to set the color of the text of the selected row to red
        Private Sub SelectRow(ByVal controlEvent As String)
            ' Parse the control event string
            Dim controlEventArray As String() = controlEvent.Split("$"c)
            ' Set the selected index of the GridView to the argument of the control event
            m_dataLayerGridView.SelectedIndex = System.Int32.Parse(controlEventArray(1))
            ' Set the text color of the selected row to red
            m_dataLayerGridView.SelectedRowStyle.ForeColor = System.Drawing.Color.Red
        End Sub

        ' Determines whether a control of the passed-in type exists in the passed-in control collection
        Private Function HasControlOfType(ByVal type As System.Type, ByVal controlCollection As System.Web.UI.ControlCollection) As Boolean
            ' Iterate through all the controls in the passed-in control collection
            For Each webControl As System.Web.UI.Control In controlCollection
                ' If the current control is of the passed-in type, return true.
                Dim webControlType As System.Type = webControl.GetType()
                If type.FullName = webControlType.FullName Then
                    Return True
                ElseIf webControl.HasControls() Then
                    ' The current control has child controls, so call this function on 
                    ' those child controls
                    'INSTANT VB NOTE: The local variable hasControlOfType was renamed since Visual Basic will not allow local variables with the same name as their method:
                    Dim hasControlOfType_Renamed As Boolean = Me.HasControlOfType(type, webControl.Controls)
                    If hasControlOfType_Renamed Then
                        Return True
                    End If
                End If
            Next webControl

            ' If the code reaches this point, no match was found. 
            Return False
        End Function

        ' Updates the WebPart instance's graphics data layer to display the passed-in table
        Private Sub CreateGraphicsDataLayer(ByVal dataTable As System.Data.DataTable)
            ' Get the functionality for the graphics resource
            Dim graphicsMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality = CType(m_adfMap.GetFunctionality(m_graphicsMapResourceID), ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality)

            ' Get a reference to the passed-in data table as a feature graphics layer by using the
            ' Web ADF's convenience method
            Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = CType(ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(dataTable, System.Drawing.Color.Blue, System.Drawing.Color.Yellow, System.Drawing.Color.Red, True), ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)

            If featureGraphicsLayer.FeatureType = ESRI.ArcGIS.ADF.Web.FeatureType.Point Then
                Dim urlRedIcon As String = Page.ClientScript.GetWebResourceUrl(Me.GetType(), "sphere-red-16x16.png")
                Dim defaultSymbol As ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol = New ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol(urlRedIcon)

                defaultSymbol.XOffset = 0
                defaultSymbol.YOffset = 8
                defaultSymbol.Height = 16
                defaultSymbol.Width = 16

                Dim defaultRenderer As ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer = New ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer(defaultSymbol)

                featureGraphicsLayer.Renderer = defaultRenderer

                Dim urlYellowIcon As String = Page.ClientScript.GetWebResourceUrl(Me.GetType(), "sphere-yellow-16x16.png")

                Dim highlightSymbol As ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol = New ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol(urlYellowIcon)

                highlightSymbol.XOffset = 0
                highlightSymbol.YOffset = 8
                highlightSymbol.Height = 16
                highlightSymbol.Width = 16

                Dim highlightRenderer As ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer = New ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer(highlightSymbol)

                featureGraphicsLayer.HighlightRenderer = highlightRenderer
            End If

            ' Set the name of the graphics layer 
            featureGraphicsLayer.TableName = m_graphicsDataLayerName

            ' Specify that the graphics layer should be drawn on the client, meaning mapTips will be
            ' displayed
            featureGraphicsLayer.RenderOnClient = True

            ' If the graphics resource already contains a table with the graphics data layer name,
            ' remove it
            If graphicsMapFunctionality.GraphicsDataSet.Tables.Contains(featureGraphicsLayer.TableName) Then
                graphicsMapFunctionality.GraphicsDataSet.Tables.Remove(featureGraphicsLayer.TableName)
            End If

            ' Retrieve the layer ID of the user-specified data layer from the dynamically created map resource
            Dim commonMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = m_adfMap.GetFunctionality(m_dataLayerMapResourceID)
            Dim layerIDs As String() = Nothing
            Dim layerNames As String() = Nothing
            Dim dataLayerID As String = Nothing
            commonMapFunctionality.GetLayers(layerIDs, layerNames)
            Dim i As Integer = 0
            Do While i < layerIDs.Length
                If layerNames(i) = m_dataLayerName Then
                    dataLayerID = layerIDs(i)
                    Exit Do
                End If
                i += 1
            Loop

            ' Get the LayerFormat of the data layer, which specifies the format of the callouts that appear
            ' when hovering over features
            Dim layerFormat As ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat = ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(m_mapResourceManager, m_dataLayerMapResourceID, dataLayerID)

            ' Loop through the LayerFormat's fields collection, copying the aliases specfied from the WebPart's
            ' GridView and hiding fields that are not in the GridView
            i = 0
            Do While i < layerFormat.Fields.Count
                Dim fieldDisplayed As Boolean = False
                Dim j As Integer = 0
                Do While j < m_dataLayerGridView.Columns.Count
                    Dim boundField As System.Web.UI.WebControls.BoundField = CType(m_dataLayerGridView.Columns(j), System.Web.UI.WebControls.BoundField)
                    If boundField.DataField = layerFormat.Fields(i).Name Then
                        layerFormat.Fields(i).Alias = boundField.HeaderText
                        layerFormat.Fields(i).Visible = True
                        fieldDisplayed = True
                        Exit Do
                    End If

                    If (Not fieldDisplayed) Then
                        layerFormat.Fields(i).Visible = False
                    End If
                    j += 1
                Loop
                i += 1
            Loop

            ' Since we want to preserve the colors specified when creating the graphics layer via the Web ADF Converter
            ' method, we have to explicitly retrieve the renderers from the graphics layer before applying the LayerFormat.
            ' Otherwise, the symbology contained in these renderers will be overwritten with whatever the LayerFormat
            ' of the data layer specifies.

            ' Transfer the selected and highlight renderers from the graphics layer to the layer format directly.
            ' While the Renderer property of a layer format defines the default renderer for GraphicFeatures on the client, 
            ' each row in the Web-tier graphics layer must be selected (the IS_SELECTED field must be true) to be rendered on the client.  
            ' If the graphics layer was rendered on the Web-tier, the selected renderer would be used.   
            ' Since a layer format does not maintain a "SelectedRenderer" property a reference to the selected rendered is stored
            ' before the layer format is applied to the graphics layer, then reset.  
            Dim selectedRenderer As ESRI.ArcGIS.ADF.Web.Display.Renderer.IRenderer = featureGraphicsLayer.SelectedRenderer

            layerFormat.Renderer = featureGraphicsLayer.Renderer
            layerFormat.HighlightRenderer = featureGraphicsLayer.HighlightRenderer

            featureGraphicsLayer.SelectedRenderer = selectedRenderer

            ' Apply the layer format
            layerFormat.Apply(featureGraphicsLayer)

            ' Add the layer to the graphics resource
            graphicsMapFunctionality.GraphicsDataSet.Tables.Add(featureGraphicsLayer)

            'featureGraphicsLayer.ForceFullClientGraphicsRefresh = true;
            m_adfMap.RefreshResource(m_graphicsMapResourceID)
        End Sub

        ' Queries the user-specified resource for features that are in the passed-in extent
        Private Function GetDataLayerFeaturesInExtent(ByVal adfExtent As ESRI.ArcGIS.ADF.Web.Geometry.Envelope) As System.Data.DataTable
            ' Get a reference to the functionality of the user-specified resource
            Dim commonMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = CType(m_adfMap.GetFunctionality(m_dataLayerMapResourceID), ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality)

            ' Get a reference to the user-specified resource
            Dim gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = commonMapFunctionality.Resource

            ' Create a query functionality to use in querying the resource
            Dim queryFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = CType(gisResource.CreateFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), Nothing), ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)

            ' Get the resource's queryable layers
            Dim layerIDs As String() = Nothing
            Dim layerNames As String() = Nothing
            queryFunctionality.GetQueryableLayers(Nothing, layerIDs, layerNames)

            ' Get the layerID of the user-specified data layer
            Dim dataLayerID As String = Nothing
            Dim index As Integer = 0
            Do While index < layerNames.Length
                If layerNames(index) = m_dataLayerName Then
                    dataLayerID = layerIDs(index)
                    Exit Do
                End If
                index += 1
            Loop

            ' Initialze a spatial filter with the passed-in extent
            Dim spatialFilter As ESRI.ArcGIS.ADF.Web.SpatialFilter = New ESRI.ArcGIS.ADF.Web.SpatialFilter()
            spatialFilter.ReturnADFGeometries = True
            spatialFilter.MaxRecords = m_maxFeatures
            spatialFilter.Geometry = adfExtent

            ' Query the data layer with the passed-in extent and return the results
            Dim resultDataTable As System.Data.DataTable = queryFunctionality.Query(commonMapFunctionality.Name, dataLayerID, spatialFilter)
            Return resultDataTable
        End Function



        Private Function propertyCheck() As Boolean
            If (Not String.IsNullOrEmpty(m_userDataSource)) AndAlso (Not String.IsNullOrEmpty(m_dataSourceType)) AndAlso (Not String.IsNullOrEmpty(m_mapResourceDefinition)) AndAlso (Not String.IsNullOrEmpty(m_dataLayerName)) AndAlso m_hasScriptManager Then
                Return True
            End If

            Return False
        End Function
#End Region

#Region "Instance Properties"

        ''' <summary>
        ''' ArcGIS Server resource type.  Valid values are "ArcGIS Server Local" and 
        ''' "ArcGIS Server Internet"
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("Valid values are 'ArcGIS Server Local' and 'ArcGIS Server Internet'"), System.Web.UI.WebControls.WebParts.WebDisplayName("ArcGIS Server Data Source Type")> _
        Public Property DataSourceType() As String
            Get
                Return m_dataSourceType
            End Get
            Set(ByVal value As String)
                m_dataSourceType = value
            End Set
        End Property

        ''' <summary>
        ''' Machine name or URL of the ArcGIS Server services host
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("ArcGIS Server machine name or Url"), System.Web.UI.WebControls.WebParts.WebDisplayName("ArcGIS Server Data Source")> _
        Public Property DataSource() As String
            Get
                Return m_userDataSource
            End Get
            Set(ByVal value As String)
                m_userDataSource = value
            End Set
        End Property

        ''' <summary>
        ''' ArcGIS Server Resource.  Must be formatted as &lt;DataFrameName&gt;@&lt;ServiceName&gt;
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("Format to use is DataFrameName@ServiceName"), System.Web.UI.WebControls.WebParts.WebDisplayName("ArcGIS Server Resource String")> _
        Public Property ResourceDefinition() As String
            Get
                Return m_mapResourceDefinition
            End Get
            Set(ByVal value As String)
                m_mapResourceDefinition = value
            End Set
        End Property

        ''' <summary>
        ''' Name of the layer for which data will be displayed in the GridView
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("Name of layer to display features in map and table"), System.Web.UI.WebControls.WebParts.WebDisplayName("Data display layer name")> _
        Public Property DataLayerName() As String
            Get
                Return m_dataLayerName
            End Get
            Set(ByVal value As String)
                m_dataLayerName = value
                m_graphicsDataLayerName = m_dataLayerName & "_Graphics"
            End Set
        End Property

        ''' <summary>
        ''' NameValueCollection of fields to display on the WebPart's GridView and when hovering over the
        ''' data layer's graphic features.  Each name in the collection specifies a database field name, 
        ''' while each value is the alias to display for that field.
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("Collection of fields to display"), System.Web.UI.WebControls.WebParts.WebDisplayName("Collection of fields to display")> _
        Public Property DisplayFields() As System.Collections.Specialized.NameValueCollection
            Get
                Return m_fieldsCollection
            End Get
            Set(ByVal value As System.Collections.Specialized.NameValueCollection)
                m_fieldsCollection = value
            End Set
        End Property

        ''' <summary>
        ''' NameValueCollection of fields to display on the WebPart's GridView and when hovering over the
        ''' data layer's graphic features.  Each name in the collection specifies a database field name, 
        ''' while each value is the alias to display for that field.
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("Comma delimited field-alias relationships, semi-colon delimited field-alias groups."), System.Web.UI.WebControls.WebParts.WebDisplayName("Collection of fields, aliases to display")> _
        Public Property DisplayFieldsString() As String
            Get
                Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder()
                If Not m_fieldsCollection Is Nothing Then
                    Dim n As Integer = 0
                    Do While n < m_fieldsCollection.Count
                        sb.Append(m_fieldsCollection.GetKey(n) & "," & m_fieldsCollection.GetValues(n)(0))
                        If n <> m_fieldsCollection.Count - 1 Then
                            sb.Append(";")
                        End If
                        n += 1
                    Loop
                End If
                Return sb.ToString()
            End Get
            Set(ByVal value As String)
                Dim fieldsCollection As System.Collections.Specialized.NameValueCollection = New System.Collections.Specialized.NameValueCollection()

                If (Not String.IsNullOrEmpty(value)) Then

                    Dim fieldAliasPairs As String() = value.Split(";".ToCharArray())
                    Dim f As Integer = 0
                    Do While f < fieldAliasPairs.Length
                        Dim fieldAlias As String() = fieldAliasPairs(f).Split(",".ToCharArray())
                        fieldsCollection.Add(fieldAlias(0), fieldAlias(1))
                        f += 1
                    Loop
                    m_fieldsCollection = fieldsCollection
                End If
            End Set
        End Property

        ''' <summary>
        ''' Maximum number of data layer features that can be displayed in the WebPart's 
        ''' graphics layer and GridView.
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("Maximum number of records to display"), System.Web.UI.WebControls.WebParts.WebDisplayName("Maximum number of records to display")> _
        Public Property MaxDataLayerFeatures() As Integer
            Get
                Return m_maxFeatures
            End Get
            Set(ByVal value As Integer)
                m_maxFeatures = value
            End Set
        End Property

        ''' <summary>
        ''' Url of the geometry service used to manage projections
        ''' </summary>
        <System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared), System.Web.UI.WebControls.WebParts.WebBrowsable(True), System.ComponentModel.Category("Resources"), System.Web.UI.WebControls.WebParts.WebDescription("ArcGIS Server Geometry Service URL"), System.Web.UI.WebControls.WebParts.WebDisplayName("ArcGIS Server Geometry Service URL")> _
        Public Property GeometryServiceUrl() As String
            Get
                Return m_geometryServiceUrl
            End Get
            Set(ByVal value As String)
                m_geometryServiceUrl = value
            End Set
        End Property

#End Region
    End Class
End Namespace