Common Custom EditorTask
Common_CustomEditorTask_CSharp\CustomEditorTask_CSharp\SearchAttributesPanel.cs
// 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.
// 

namespace CustomEditorTask_CSharp
{
    [AjaxControlToolkit.ClientScriptResource("SearchAttributesPanel.js", "CustomEditorTask_CSharp.javascript.SearchAttributesPanel.js")]
    class SearchAttributesPanel : ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorPanel
    {
        #region Instance Variable Declarations

        private System.Web.UI.HtmlControls.HtmlGenericControl m_panelDiv;
        private System.Collections.Specialized.NameValueCollection m_callbackArgsCollection;

        #endregion

        #region Constructor

        public SearchAttributesPanel(ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorTask editorTask) :
            base("Search", editorTask, "searchAttributesPanel")
        {
            // Add a handler that fires when a new editor layer is selected
            this.ParentEditor.LayerChanged +=
                new ESRI.ArcGIS.ADF.ArcGISServer.Editor.Editor.LayerChangedHandler(ParentEditor_LayerChanged);
        }

        #endregion

        #region WebControl Life Cycle Event Overrides

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
         
            // Create a container div for the client-side UI component
            m_panelDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
            m_panelDiv.ID = "searchAttributesPanelDiv";
            Controls.Add(m_panelDiv);
        }

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

            // Register the JavaScript file containing the SearchAttributesPanel JavaScript UI 
            // component and functions to control interaction between the client panel and the server
            //System.Web.UI.ScriptManager.RegisterClientScriptResource((System.Web.UI.Control)this, 
            //    this.GetType(), "CustomEditorTask_CSharp.javascript.SearchAttributesPanel.js");


            //this.Page.ClientScript.RegisterClientScriptResource(this.GetType(), "CustomEditorTask_CSharp.javascript.SearchAttributesPanel.js");

            // Get a reference to the editor task's layers drop-down list
            System.Web.UI.WebControls.DropDownList editorLayerList = null;
            editorLayerList = CustomUtilities.FindControl(
                 "editTaskLayerList", this.ParentEditor as System.Web.UI.Control) as
                 System.Web.UI.WebControls.DropDownList;

            // Get strings containing the numeric and text fields for the current layer
            string numericFields = CustomUtilities.GetFields(CustomEditorInstance, 
                CustomUtilities.FieldType.Numeric);
            string textFields = CustomUtilities.GetFields(CustomEditorInstance, 
                CustomUtilities.FieldType.Text);

            // Get the URL of the activity_indicator image file
            string activityIndicatorUrl = this.Page.ClientScript.GetWebResourceUrl(
                typeof(CustomEditorTask), "CustomEditorTask_CSharp.images.activity_indicator.gif");


            string csname1 = this.ClientID + "_init";

            if (!Page.ClientScript.IsStartupScriptRegistered(this.GetType(), csname1))
            {
                // Construct the JavaScript necessary to initialize the client-side search panel with
                // server-side properties
                string scriptBlock = @"                
                    function invokeSearchPanelInitialization(){{
                      initializeSearchPanel('{0}', '{1}', '{2}', '{3}', '{4}', ""{5}"");
                    }}
                    Sys.Application.add_init(invokeSearchPanelInitialization);";
                scriptBlock = string.Format(scriptBlock, m_panelDiv.ClientID, editorLayerList.ClientID,
                    numericFields, textFields, activityIndicatorUrl, this.CallbackFunctionString);

                // Register the initialization code as a startup script
                this.Page.ClientScript.RegisterStartupScript(this.GetType(), this.ClientID + "_init",
                    scriptBlock, true);
            }

            // Initialize the OIDFieldName property
            this.OIDfieldName = CustomEditorInstance.FeatureLayer.FeatureClass.OIDFieldName;
        }

        protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
        {
            base.RenderContents(writer);

            // Create a callback result that will call the renderSearchPanel JavaScript function, which
            // will expicitly re-render the client-side search panel.  This is necessary because the
            // server has no knowledge of the client-side panel's elements, so when the panel is rendered
            // (which is done with information stored in state on the server), these elements are lost.
            ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult renderSearchPanelCallbackResult =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript("renderSearchPanel();");
            this.ParentEditor.CallbackResults.Add(renderSearchPanelCallbackResult);
        }

        #endregion

        #region Web ADF Event Handlers

        // Fires when the user selects a different layer from the editor task's layers drop-down list
        void ParentEditor_LayerChanged(ESRI.ArcGIS.Carto.IFeatureLayer featureLayer)
        {
            // Get the object ID field of the newly selected layer
            this.OIDfieldName = featureLayer.FeatureClass.OIDFieldName;

            // Get the numeric and text fields of the new layer
            string numericFields = CustomUtilities.GetFields(CustomEditorInstance, 
                CustomUtilities.FieldType.Numeric);
            string textFields = CustomUtilities.GetFields(CustomEditorInstance, 
                CustomUtilities.FieldType.Text);

            // Construct a call to a JavaScript function that will update the client-side search 
            // panel with the new layer's fields
            string jsUpdateFields = string.Format("updateSearchPanelFields('{0}', '{1}');",
                textFields, numericFields);
            ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult updateFieldsCallbackResult =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsUpdateFields);
            this.ParentEditor.CallbackResults.Add(updateFieldsCallbackResult);
        }

        #endregion;

        #region Callback Handler

        public override void RaiseCallbackEvent(string eventArgs)
        {
            // Parse callback arguments using the CallbackUtility included with Web ADF.
            m_callbackArgsCollection = 
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(eventArgs);
            
            // Check whether the current event argument indicates that a search was initiated by the user
            if (m_callbackArgsCollection["EventArg"] == "search")
            {
                // Get the fields selected by the user, with aliases replaced by database field names
                string searchFieldsString = CustomUtilities.ReplaceAliases(
                    m_callbackArgsCollection["Fields"], CustomEditorInstance);

                // Parse the fields string into an array
                char[] delimiter = { ';' };
                string[] searchFieldsArray = searchFieldsString.Split(delimiter, 
                    System.StringSplitOptions.RemoveEmptyEntries);

                // Build a query string based on the selected fields, operator, and search value
                string queryString = this.BuildQuery(m_callbackArgsCollection["Operator"], 
                    searchFieldsArray, m_callbackArgsCollection["Value"]);

                // Get IDs of features satisfying query
                int[] matchingFeatureIDs = CustomUtilities.DoQuery(queryString, this.ParentEditor);

                // Get LayerDescription object for currently selected layer
                ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription agsLayerDescription =
                    ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorUtilities.GetLayerDescription(
                    this.ParentEditor.MapFunctionality, this.ParentEditor.SelectedLayerID);

                // Combine the feature IDs from the query with those currently selected.  How this
                // is done will depend on the selection option currently specified in the editor
                // task's settings
                bool selectionChanged;
                System.Collections.Generic.List<int> selectedIDsList =
                    CustomUtilities.UpdateSelection(agsLayerDescription.SelectionFeatures,
                    matchingFeatureIDs, out selectionChanged, this.ParentEditor.EditorTask);

                // Check whether the selected IDs changed
                if (selectionChanged)
                {
                    // Set selected features on layer currently being edited
                    agsLayerDescription.SelectionFeatures = selectedIDsList.ToArray();

                    // Get attributes panel and set to edit the attributes of selected features
                    ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditAttributesPanel editAttributesPanel =
                        this.ParentEditor.AttributesEditor as 
                        ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditAttributesPanel;
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResultCollection editAttributesPanelResults =
                        editAttributesPanel.SetSelectedFeatures(agsLayerDescription.SelectionFeatures);
                    this.CallbackResults.CopyFrom(editAttributesPanelResults);

                    // Refresh map
                    ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorUtilities.RefreshMap(this.ParentEditor, 
                        this.CallbackResults);

                    // Refresh toolbars
                    this.ParentEditor.RefreshToolbars(this.CallbackResults);
                }

                // Create callback result that calls JavaScript to hide AJAX activity indicator
                string jsHideIndicator = "hideSearchingIndicator();";
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult hideIndicatorCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsHideIndicator);
                this.CallbackResults.Add(hideIndicatorCallbackResult);
            }
            else
            {
                base.RaiseCallbackEvent(eventArgs);
            }
        }

        public override string GetCallbackResult()
        {
            // If the callback was initiated by a search operation, return the panel's callback results
            if (m_callbackArgsCollection["EventArg"] == "search")
                return this.CallbackResults.ToString();
            else
                return base.GetCallbackResult();
        }

        #endregion

        #region Instance Properties

        // Name of object ID field of selected layer
        protected string OIDfieldName
        {
            get
            {
                return (string)StateManager.GetProperty("selectedLayerOIDfield");
            }
            set
            {
                StateManager.SetProperty("selectedLayerOIDfield", value);
            }
        }

        // Easy access to the CustomEditor containing the panel instance
        protected CustomEditor CustomEditorInstance
        {
            get { return (CustomEditor)this.ParentEditor; }
        }

        #endregion

        #region Instance Methods

        // Builds a query string from search parameters
        private string BuildQuery(string searchType, string[] searchFields, string searchValue)
        {
            string queryString = "";
            string[] searchWords;
            // Check the specified search type
            switch (searchType)
            {
                case "All words":
                    // Parse the words contained in the search value and place them in an array.  
                    // The Split method uses the space character as a default delimiter.
                    searchWords = searchValue.Split();

                    // Since we only want to find features that contain all the words of the search 
                    // value, we iterate through the search words and build the query to extract 
                    // features that contain each word in any field.  
                    for (int i = 0; i < searchWords.Length; i++)
                    {
                        queryString += "(";
                        for (int j = 0; j < searchFields.Length; j++)
                        {
                            queryString += string.Format("({0} LIKE '%{1}%')", searchFields[j], 
                                searchWords[i]);
                            if (j < searchFields.Length - 1)
                                queryString += " OR ";
                        }
                        queryString += ")";
                        if (i < searchWords.Length - 1)
                            queryString += " AND ";
                    }
                    break;
                case "Any words":
                    // Parse the words contained in the search value and place them in an array.  
                    // The Split method uses the space character as a default delimiter.
                    searchWords = searchValue.Split();

                    // Since we want to find features that contain any of the words of the search 
                    // value, we iterate through the search words and build the query to extract 
                    // features that contain any word in any field.  
                    for (int i = 0; i < searchWords.Length; i++)
                    {
                        for (int j = 0; j < searchFields.Length; j++)
                        {
                            queryString += string.Format("({0} LIKE '%{1}%')", searchFields[j], 
                                searchWords[i]);
                            if ((j < searchFields.Length - 1) || (i < searchWords.Length - 1))
                                queryString += " OR ";
                        }
                    }
                    break;
                case "Exact phrase":
                    // Since we only want to find features that contain the entire search value as
                    // specified by the user, we iterate through the search fields and build the 
                    // query to extract features that contain the whole search value in any field.  
                    for (int i = 0; i < searchFields.Length; i++)
                    {
                        queryString += string.Format("({0} LIKE '%{1}%')", searchFields[i], 
                            searchValue);
                        if (i < searchFields.Length - 1)
                            queryString += " OR ";
                    }
                    break;
                default:
                    string queryOperator = null;
                    // For numeric operators, check the search type and initialize a variable 
                    // storing the corresponding logical operator
                    switch (searchType)
                    {
                        case "Equal to":
                            queryOperator = "=";
                            break;
                        case "Greater than":
                            queryOperator = ">";
                            break;
                        case "Less than":
                            queryOperator = "<";
                            break;
                    }

                    // Iterate through the search fields and build a query to extract features 
                    // that have at least one value satisfying the relationship to the search
                    // value specified by the query operator
                    for (int i = 0; i < searchFields.Length; i++)
                    {
                        queryString += string.Format("({0} {1} {2})", searchFields[i], 
                            queryOperator, searchValue);
                        if (i < searchFields.Length - 1)
                            queryString += " OR ";
                    }
                    break;
            }

            return queryString;
        }

        #endregion
    }

}