Common Custom controls
CustomControls_CSharp\ADFWebPart\MapGridViewWebPart.cs
// Copyright 2011 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.
// 


namespace ADFWebPart
{
    // 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(typeof(ADFWebPart.MapGridViewWebPartDesigner),
        typeof(System.ComponentModel.Design.IDesigner))]
    public class MapGridViewWebPart : AJAXSharePointWebPart
    {
        #region Instance Variable Declarations

        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Map m_adfMap = null;
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager m_mapResourceManager = null;
        private System.Web.UI.UpdatePanel m_updatePanel = null;
        private string m_graphicsDataLayerName = null;
        private System.Web.UI.WebControls.GridView m_dataLayerGridView = null;
        private bool m_gridViewDataBound = false;
        private bool m_hasScriptManager = false;
        private string m_graphicsMapResourceID;
        private string m_dataLayerMapResourceID;
        private string m_backgroundMapResourceID;


        private string m_dataSourceType;
        private string m_userDataSource;
        private string m_mapResourceDefinition;
        private string m_dataLayerName;
        private string m_geometryServiceUrl;
        private int m_maxFeatures;
        private System.Collections.Specialized.NameValueCollection m_fieldsCollection;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new, uninitialized instance of the MapGridViewWebPart class.
        /// </summary>
        public MapGridViewWebPart()
        {
            // 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");
        }

        /// <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 MapGridViewWebPart(string dataSource, string resourceDefinition, string resourceType,
            string dataLayerName, string geometryServiceUrl)
        {
            m_userDataSource = dataSource;
            m_mapResourceDefinition = resourceDefinition;
            m_dataSourceType = resourceType;
            m_dataLayerName = dataLayerName;
            m_graphicsDataLayerName = m_dataLayerName + "_Graphics";
            m_geometryServiceUrl = geometryServiceUrl;
        }
        #endregion

        #region WebControl Life Cycle Event Handlers

        protected override void OnInit(System.EventArgs e)
        {
            base.OnInit(e);

            m_graphicsMapResourceID = "GraphicsMapResource<!--" + this.UniqueID + "-->";
            m_dataLayerMapResourceID = "AGSMapResource<!--" + this.UniqueID + "-->";
            m_backgroundMapResourceID = "AGSBackground<!--" + this.UniqueID + "-->";

            //Make sure there is a script manager on the page
            if (System.Web.UI.ScriptManager.GetCurrent(this.Page) != null)
            {
                m_hasScriptManager = true;

            }
        }

        // Handles custom postback row click event, if enabled
        protected override void OnLoad(System.EventArgs e)
        {
            base.OnLoad(e);

            // Make sure the page has a script manager
            if (propertyCheck())
            {
                // 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)
                System.Collections.Specialized.NameValueCollection requestParameters =
                    Page.Request.Params;
                string controlEvent = null;
                string controlID = 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 (!string.IsNullOrEmpty(controlID) && controlID.Contains(m_dataLayerGridView.ID))
                {
                    // 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$"))
                        SelectRow(controlEvent);
                }
            }
        }

        // Creates the controls contained in the WebPart - A Map, GridView, and UpdatePanel
        protected override void CreateChildControls()
        {
            try
            {
                base.CreateChildControls();
                // Make sure the page has a ScriptManager.  This is required for managing the 
                // asynchronous Map-GridView interaction via an UpdatePanel
                if (propertyCheck())
                {
                    // 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 = this.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;
                    m_adfMap.ExtentChanged +=
                        new ESRI.ArcGIS.ADF.Web.UI.WebControls.MapExtentChangeEventHandler(
                            Map_ExtentChanged);

                    // Create an HTML table to hold the Map and GridView
                    System.Web.UI.WebControls.Table 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
                    System.Web.UI.WebControls.TableRow tableRow;
                    tableRow = new System.Web.UI.WebControls.TableRow();
                    table.Rows.Add(tableRow);

                    // Add a cell to the table and put the Map in it
                    System.Web.UI.WebControls.TableCell 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 = this.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
                    System.Web.UI.AsyncPostBackTrigger mapTrigger =
                        new System.Web.UI.AsyncPostBackTrigger();
                    // Set the trigger control to be the Map
                    mapTrigger.ControlID = this.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 (!m_hasScriptManager)
                    {
                        System.Web.UI.WebControls.Label noScriptManagerLabel =
                            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
                    {
                        System.Web.UI.WebControls.Label noResourceDefLabel =
    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);
                    }
                }
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Debug.Write(ex.Message);
            }
        }

        // Executes before the control is rendered
        protected override void OnPreRender(System.EventArgs e)
        {
            base.OnPreRender(e);

            if (propertyCheck())
            {
                // Add resources to the map resource manager
                ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem mapResourceItem =
                    AddResourcesToMapResourceManager();

                // Initialize the map's extent based on the user-specified data layer
                if (m_mapResourceManager != null && mapResourceItem != null)
                {
                    InitializeMapExtent(mapResourceItem);
                }

                // Check whether the WebPart user has specified fields for display
                m_dataLayerGridView.Columns.Clear();
                if (m_fieldsCollection != null)
                {
                    // Create BoundFields for each of the fields specified by the user and add them to the GridView
                    for (int i = 0; i < m_fieldsCollection.Count; i++)
                    {
                        System.Web.UI.WebControls.BoundField 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);
                    }

                    // 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;
                }

                // Get the functionality for the graphics resource
                ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality graphicsMapFunctionality =
                    (ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality)m_adfMap.GetFunctionality(
                    m_graphicsMapResourceID);

                // 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 ((graphicsMapFunctionality != null) &&
                    !graphicsMapFunctionality.GraphicsDataSet.Tables.Contains(m_graphicsDataLayerName))
                {
                    // 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.
                    int maxFeatures = m_maxFeatures;
                    m_maxFeatures = int.MaxValue;
                    System.Data.DataTable resultsDataTable = GetDataLayerFeaturesInExtent(null);
                    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)
                        resultsDataTable = GetDataLayerFeaturesInExtent(null);
                    else
                        resultsDataTable = GetDataLayerFeaturesInExtent(m_adfMap.Extent);

                    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.  
                else if (!m_gridViewDataBound)
                {
                    try
                    {
                        // Get the data layer features in the new extent and update the graphics data
                        // layer accordingly
                        System.Data.DataTable queryResultsDataTable =
                            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 ((m_adfMap.Page.Session["totalDataLayerFeatures"] != null) &&
                            ((int)m_adfMap.Page.Session["totalDataLayerFeatures"] > m_maxFeatures))
                            CreateGraphicsDataLayer(queryResultsDataTable);

                        // Store the results table in session for use when the GridView is paging
                        string resultsTableSessionKey = string.Format("{0}_queryResultsDataTable", this.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 = null;

                        // Bind the data source to the GridView
                        m_dataLayerGridView.DataBind();
                    }
                    catch (System.Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex.Message);
                    }
                }
            }
        }

        // Renders the web control on the page
        protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
        {
            // Check whether the control is being rendered at design-time or run-time
            if (DesignMode)
            {
                // 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
                System.Web.UI.WebControls.Label IDLabel =
                    new System.Web.UI.WebControls.Label();
                IDLabel.Font.Name = "Verdana";
                IDLabel.Font.Size = 9;
                IDLabel.Text = "&nbsp;" + this.ID + "<br /><br />";
                // Render the label
                IDLabel.RenderControl(writer);


                // Initialize a label with the control type
                System.Web.UI.WebControls.Label typeLabel =
                    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
                bool hasScriptManager = HasControlOfType(
                    typeof(System.Web.UI.ScriptManager), Page.Controls);
                if (!hasScriptManager)
                {
                    // Format warning labels

                    // Create a label in red, bold font for the warning title
                    System.Web.UI.WebControls.Label WarningTitleLabel =
                        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
                    System.Web.UI.WebControls.Label WarningDescriptionLabel =
                        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);
                }

            }
            else
            {
                base.RenderContents(writer);
            }
        }

        #endregion

        #region Child Control Event Handlers

        // Fires when the extent of the WebPart's Map Control changes
        private void Map_ExtentChanged(object sender,
            ESRI.ArcGIS.ADF.Web.UI.WebControls.ExtentEventArgs extentEventArgs)
        {
            // Make sure the event is not firing on map load (OldExtent == null) and the old and
            // new extents are distinct
            if (extentEventArgs.OldExtent != null &&
                !extentEventArgs.OldExtent.Equals(extentEventArgs.NewExtent))
            {
                try
                {
                    // Get the data layer features in the new extent and update the graphics data
                    // layer accordingly
                    System.Data.DataTable queryResultsDataTable =
                        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 ((m_adfMap.Page.Session["totalDataLayerFeatures"] != null) &&
                        ((int)m_adfMap.Page.Session["totalDataLayerFeatures"] > m_maxFeatures))
                        CreateGraphicsDataLayer(queryResultsDataTable);

                    // Store the results table in session for use when the GridView is paging
                    string resultsTableSessionKey = string.Format("{0}_queryResultsDataTable", this.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 = null;

                    // Bind the data source to the GridView
                    m_dataLayerGridView.DataBind();
                }
                catch (System.Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }

        // Adds interaction with the Map to each GridView row's mouseOver and click events
        private void GridView_RowDataBound(object sender,
            System.Web.UI.WebControls.GridViewRowEventArgs gridViewRowEventArgs)
        {
            // Make sure the row is a valid DataRow
            if (gridViewRowEventArgs.Row.RowType == System.Web.UI.WebControls.DataControlRowType.DataRow)
            {
                // Get the graphics resource from the map
                ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality graphicsMapFunctionality =
                    (ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality)m_adfMap.GetFunctionality(
                    m_graphicsMapResourceID);

                // 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))
                {
                    // Get the current row
                    System.Web.UI.WebControls.GridViewRow gridViewRow = gridViewRowEventArgs.Row;

                    // Get a reference to the DataLayer as a Web ADF GraphicsLayer
                    ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer adfGraphicsLayer =
                        graphicsMapFunctionality.GraphicsDataSet.Tables[m_graphicsDataLayerName] as
                        ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer;

                    // Get the client-side ID of the graphics layer 
                    string dataLayerID = m_adfMap.GetGraphicsLayerClientID(adfGraphicsLayer);

                    // Find the index of the first unique ID column in the graphics layer's table
                    int uniqueIDindex = -1;
                    for (int i = 0; i < adfGraphicsLayer.Columns.Count; i++)
                    {
                        if (adfGraphicsLayer.Columns[i].Unique)
                        {
                            uniqueIDindex = i;
                            break;
                        }
                    }

                    // Get the index of the row in the graphics layer's table that has a unique ID
                    // matching that of the current row
                    System.Data.DataRowView dataRowView = gridViewRow.DataItem as System.Data.DataRowView;
                    int graphicFeatureIndex = -1;
                    for (int i = 0; i < adfGraphicsLayer.Rows.Count; i++)
                    {
                        if (adfGraphicsLayer.Rows[i][uniqueIDindex].Equals(
                            dataRowView[uniqueIDindex]))
                        {
                            graphicFeatureIndex = i;
                            break;
                        }
                    }

                    // 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
                    string jsSetRowAndFeatureHighlight = "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
                    string jsZoomToFeature = 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);
                }
            }
        }

        // Fires when the Page is changed in the GridView
        private void GridView_PageIndexChanging(object sender,
            System.Web.UI.WebControls.GridViewPageEventArgs gridViewPageEventArgs)
        {
            // Get a reference to the GridView
            System.Web.UI.WebControls.GridView gridView = sender as 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
            string resultsTableSessionKey = string.Format("{0}_queryResultsDataTable", this.ID);
            System.Data.DataTable resultsDataTable = Page.Session[resultsTableSessionKey] as
                System.Data.DataTable;
            gridView.DataSource = resultsDataTable;
            gridView.DataBind();
        }

        #endregion

        #region Child Control Initialization Methods

        // Creates and initializes a GridView for use in the WebPart
        private System.Web.UI.WebControls.GridView CreateGridView()
        {
            // Create the GridView and set dimensional/positional attributes
            System.Web.UI.WebControls.GridView gridView = new System.Web.UI.WebControls.GridView();
            gridView = new System.Web.UI.WebControls.GridView();
            gridView.ID = this.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.
            gridView.RowDataBound += new System.Web.UI.WebControls.GridViewRowEventHandler(
                GridView_RowDataBound);

            gridView.DataBound += new System.EventHandler(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.
            gridView.PageIndexChanging += new
                System.Web.UI.WebControls.GridViewPageEventHandler(GridView_PageIndexChanging);

            return gridView;
        }

        void GridView_DataBound(object sender, System.EventArgs e)
        {
            m_gridViewDataBound = true;
        }

        // Adds map resources to the map resource manager
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem AddResourcesToMapResourceManager()
        {
            // 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
            ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem mapResourceItem = null;
            ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem backgroundResourceItem = null;

            // Make sure the map resource manager exists and that it does not contain any resource
            // items before initializing
            if (m_mapResourceManager != null && m_mapResourceManager.ResourceItems.Count == 0)
            {
                // Create the background resource item and initialize it to display the 
                // ESRI_StreetMap_World_2D ArcGIS Online service
                ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition gisBackgroundItemDefinition =
                    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
                ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition 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
                string graphicsDataSourceDefinition = "In Memory";
                string graphicsDataSourceType = "GraphicsLayer";

                ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition
                    graphicsResourceItemDefinition =
                    Utility.CreateGISResourceItemDefinition(graphicsDataSourceDefinition,
                    graphicsDataSourceType, "", "", true);
                ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem graphicsResourceItem =
                    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);
            }
            else if (m_mapResourceManager.ResourceItems.Count > 0)
            {
                ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDefinition gisResourceItemDefinition =
                    Utility.CreateGISResourceItemDefinition(m_userDataSource, m_dataSourceType, string.Empty,
                    m_mapResourceDefinition, true);

                ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem existingMapResourceItem = m_mapResourceManager.ResourceItems.Find(m_dataLayerMapResourceID);
                string existingResourceItemDefinition = 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())
                {
                    return mapResourceItem;
                }

                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);
            }

            return mapResourceItem;
        }

        // Initializes the map extent to the default extent of the passed-in map resource item
        private void InitializeMapExtent(ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem
            mapResourceItem)
        {
            // Get the resource from the passed-in resource item
            ESRI.ArcGIS.ADF.Web.DataSources.IMapResource commonMapResource =
                mapResourceItem.Resource as ESRI.ArcGIS.ADF.Web.DataSources.IMapResource;

            if (commonMapResource != null)
            {
                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                    
                    ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsResourceSpatialReference =
                        ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.FromAdfSpatialReference(
                        commonMapResource.MapInformation.DefaultSpatialReference);
                    ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsMapSpatialReference =
                        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
                    ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN agsResourceExtent =
                        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
                    ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsInGeometryArray;
                    agsInGeometryArray = new ESRI.ArcGIS.ADF.ArcGISServer.Geometry[1];
                    agsInGeometryArray[0] = agsResourceExtent;

                    // Get a reference to the geometry service specified by the geometry service
                    // URL property
                    ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy geometryServer =
                        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
                    ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsOutGeometryArray = geometryServer.Project(
                        agsResourceSpatialReference, agsMapSpatialReference, false, null, null,
                        agsInGeometryArray);

                    // Get the projected extent from the output geometry array
                    agsResourceExtent = agsOutGeometryArray[0] as 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);
                }
            }
        }

        #endregion

        #region Other Instance Methods

        // Method to set the color of the text of the selected row to red
        private void SelectRow(string controlEvent)
        {
            // Parse the control event string
            string[] controlEventArray = controlEvent.Split('$');
            // 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;
        }

        // Determines whether a control of the passed-in type exists in the passed-in control collection
        private bool HasControlOfType(System.Type type,
            System.Web.UI.ControlCollection controlCollection)
        {
            // Iterate through all the controls in the passed-in control collection
            foreach (System.Web.UI.Control webControl in controlCollection)
            {
                // If the current control is of the passed-in type, return true.
                System.Type webControlType = webControl.GetType();
                if (type.FullName == webControlType.FullName)
                    return true;
                else if (webControl.HasControls())
                {
                    // The current control has child controls, so call this function on 
                    // those child controls
                    bool hasControlOfType = HasControlOfType(type, webControl.Controls);
                    if (hasControlOfType)
                        return true;
                }
            }

            // If the code reaches this point, no match was found. 
            return false;
        }

        // Updates the WebPart instance's graphics data layer to display the passed-in table
        private void CreateGraphicsDataLayer(System.Data.DataTable dataTable)
        {
            // Get the functionality for the graphics resource
            ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality graphicsMapFunctionality =
                (ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapFunctionality)m_adfMap.GetFunctionality(
                m_graphicsMapResourceID);

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

            if (featureGraphicsLayer.FeatureType == ESRI.ArcGIS.ADF.Web.FeatureType.Point)
            {
                string urlRedIcon = Page.ClientScript.GetWebResourceUrl(GetType(),
                    "ADFWebPart.images.sphere-red-16x16.png");
                ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol defaultSymbol =
                    new ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol(urlRedIcon);

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

                ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer defaultRenderer =
                    new ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer(defaultSymbol);

                featureGraphicsLayer.Renderer = defaultRenderer;

                string urlYellowIcon = Page.ClientScript.GetWebResourceUrl(GetType(),
                    "ADFWebPart.images.sphere-yellow-16x16.png");

                ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol highlightSymbol =
                    new ESRI.ArcGIS.ADF.Web.Display.Symbol.RasterMarkerSymbol(urlYellowIcon);

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

                ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer highlightRenderer =
                    new ESRI.ArcGIS.ADF.Web.Display.Renderer.SimpleRenderer(highlightSymbol);

                featureGraphicsLayer.HighlightRenderer = highlightRenderer;
            }

            // 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))
                graphicsMapFunctionality.GraphicsDataSet.Tables.Remove(featureGraphicsLayer.TableName);

            // Retrieve the layer ID of the user-specified data layer from the dynamically created map resource
            ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality = m_adfMap.GetFunctionality(m_dataLayerMapResourceID);
            string[] layerIDs = null;
            string[] layerNames = null;
            string dataLayerID = null;
            commonMapFunctionality.GetLayers(out layerIDs, out layerNames);
            for (int i = 0; i < layerIDs.Length; i++)
            {
                if (layerNames[i] == m_dataLayerName)
                {
                    dataLayerID = layerIDs[i];
                    break;
                }
            }

            // Get the LayerFormat of the data layer, which specifies the format of the callouts that appear
            // when hovering over features
            ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat 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
            for (int i = 0; i < layerFormat.Fields.Count; i++)
            {
                bool fieldDisplayed = false;
                for (int j = 0; j < m_dataLayerGridView.Columns.Count; j++)
                {
                    System.Web.UI.WebControls.BoundField boundField =
                        (System.Web.UI.WebControls.BoundField)m_dataLayerGridView.Columns[j];
                    if (boundField.DataField == layerFormat.Fields[i].Name)
                    {
                        layerFormat.Fields[i].Alias = boundField.HeaderText;
                        layerFormat.Fields[i].Visible = true;
                        fieldDisplayed = true;
                        break;
                    }

                    if (!fieldDisplayed)
                        layerFormat.Fields[i].Visible = false;
                }
            }

            // 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.  
            ESRI.ArcGIS.ADF.Web.Display.Renderer.IRenderer selectedRenderer = 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);
        }

        // Queries the user-specified resource for features that are in the passed-in extent
        private System.Data.DataTable GetDataLayerFeaturesInExtent(ESRI.ArcGIS.ADF.Web.Geometry.Envelope adfExtent)
        {
            // Get a reference to the functionality of the user-specified resource
            ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
              (ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality)m_adfMap.GetFunctionality(m_dataLayerMapResourceID);

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

            // Create a query functionality to use in querying the resource
            ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality queryFunctionality
                = (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)gisResource.CreateFunctionality
                (typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), null);

            // Get the resource's queryable layers
            string[] layerIDs;
            string[] layerNames;
            queryFunctionality.GetQueryableLayers(null, out layerIDs, out layerNames);

            // Get the layerID of the user-specified data layer
            string dataLayerID = null;
            for (int index = 0; index < layerNames.Length; index++)
            {
                if (layerNames[index] == m_dataLayerName)
                {
                    dataLayerID = layerIDs[index];
                    break;
                }
            }

            // Initialze a spatial filter with the passed-in extent
            ESRI.ArcGIS.ADF.Web.SpatialFilter 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
            System.Data.DataTable resultDataTable = queryFunctionality.Query(
                commonMapFunctionality.Name, dataLayerID, spatialFilter);
            return resultDataTable;
        }



        private bool propertyCheck()
        {
            if (!string.IsNullOrEmpty(m_userDataSource) && !string.IsNullOrEmpty(m_dataSourceType) &&
                !string.IsNullOrEmpty(m_mapResourceDefinition) && !string.IsNullOrEmpty(m_dataLayerName) &&
                m_hasScriptManager)
            {
                return true;
            }

            return false;
        }
        #endregion

        #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 string DataSourceType
        {
            get
            {
                return m_dataSourceType;
            }
            set
            {
                m_dataSourceType = value;
            }
        }

        /// <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 string DataSource
        {
            get
            {
                return m_userDataSource;
            }
            set
            {
                m_userDataSource = value;
            }
        }

        /// <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 string ResourceDefinition
        {
            get
            {
                return m_mapResourceDefinition;
            }
            set
            {
                m_mapResourceDefinition = value;
            }
        }

        /// <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 string DataLayerName
        {
            get
            {
                return m_dataLayerName;
            }
            set
            {
                m_dataLayerName = value;
                m_graphicsDataLayerName = m_dataLayerName + "_Graphics";
            }
        }

        /// <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 System.Collections.Specialized.NameValueCollection DisplayFields
        {
            get
            {
                return m_fieldsCollection;
            }
            set
            {
                m_fieldsCollection = value;
            }
        }

        /// <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 string DisplayFieldsString
        {
            get
            {
                System.Text.StringBuilder sb = new System.Text.StringBuilder();
                if (m_fieldsCollection != null)
                {
                    for (int n = 0; n < m_fieldsCollection.Count; n++)
                    {
                        sb.Append(m_fieldsCollection.GetKey(n) + "," + m_fieldsCollection.GetValues(n)[0]);
                        if (n != m_fieldsCollection.Count - 1)
                        {
                            sb.Append(";");
                        }
                    }
                }
                return sb.ToString();
            }
            set
            {
                System.Collections.Specialized.NameValueCollection fieldsCollection =
new System.Collections.Specialized.NameValueCollection();

                if (!string.IsNullOrEmpty(value))
                {

                    string[] fieldAliasPairs = value.Split(";".ToCharArray());
                    for (int f = 0; f < fieldAliasPairs.Length; f++)
                    {
                        string[] fieldAlias = fieldAliasPairs[f].Split(",".ToCharArray());
                        fieldsCollection.Add(fieldAlias[0], fieldAlias[1]);
                    }
                    m_fieldsCollection = fieldsCollection;
                }
            }
        }

        /// <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 int MaxDataLayerFeatures
        {
            get
            {
                return m_maxFeatures;
            }
            set
            {
                m_maxFeatures = value;
            }
        }

        /// <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 string GeometryServiceUrl
        {
            get
            {
                return m_geometryServiceUrl;
            }
            set
            {
                m_geometryServiceUrl = value;
            }
        }

        #endregion
    }
}