Common Custom EditorTask
Common_CustomEditorTask_CSharp\CustomEditorTask_CSharp\QueryBuilderPanel.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("QueryBuilderPanel.js", "CustomEditorTask_CSharp.javascript.QueryBuilderPanel.js")]    
    class QueryBuilderPanel : ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorPanel
    {
        #region Instance Variable Declarations

        private System.Web.UI.WebControls.Label m_lblFields;
        private System.Web.UI.WebControls.DropDownList m_ddFields;
        private System.Web.UI.WebControls.Label m_lblOperator;
        private System.Web.UI.WebControls.DropDownList m_ddOperators;
        private System.Web.UI.WebControls.Label m_lblValues;
        private System.Web.UI.WebControls.DropDownList m_ddValues;
        private System.Web.UI.WebControls.Button m_btnAddToQuery;
        private System.Web.UI.WebControls.Button m_btnAnd;
        private System.Web.UI.WebControls.Button m_btnOr;
        private System.Web.UI.WebControls.Button m_btnNot;
        private System.Web.UI.WebControls.Button m_btnLtPara;
        private System.Web.UI.WebControls.Button m_btnRtPara;
        private System.Web.UI.WebControls.Button m_btnPct;
        private System.Web.UI.WebControls.Button m_btnUnderscore;
        private System.Web.UI.WebControls.TextBox m_txtQuery;
        private System.Web.UI.WebControls.Button m_btnClearQuery;
        private System.Web.UI.WebControls.Button m_btnDoQuery;
        
        #endregion

        #region Constructor

        public QueryBuilderPanel(ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorTask editorTask) : 
            base("Query Builder", editorTask, "queryBuilderPanel") 
        {
            this.ParentEditor.LayerChanged += 
                new ESRI.ArcGIS.ADF.ArcGISServer.Editor.Editor.LayerChangedHandler(ParentEditor_LayerChanged);
        }

        #endregion

        #region WebControl Life Cycle Event Overrides

        protected override void OnInit(System.EventArgs eventArgs)
        {
            base.OnInit(eventArgs);
            // Set height to limit the amount of screen space occupied by the panel
            this.Height = System.Web.UI.WebControls.Unit.Pixel(200);
        }

        // Instantiate the panel's child controls
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Instantiate panel controls and add them to the custom panel's controls collection

            // Label for the Fields drop-down
            m_lblFields = new System.Web.UI.WebControls.Label();
            m_lblFields.ID = "lblFields";
            m_lblFields.Text = "Fields:";
            Controls.Add(m_lblFields);

            // Fields drop-down
            m_ddFields = new System.Web.UI.WebControls.DropDownList();
            m_ddFields.ID = "ddFields";

            // Initialization of items in the fields drop-down.  Necessary include logic for doing this here
            // in case only one version is available for editing.  In that case, the controls will not be
            // instantiated before the layer changed event fires
            bool fieldsInit = false;
            if (CustomEditorInstance.FeatureLayer != null)
            {
                // Get fields and add to fields drop-down
                string fields = CustomUtilities.GetFields(CustomEditorInstance);
                char[] delimiter = { ':' };
                string[] fieldList = fields.Split(delimiter, System.StringSplitOptions.RemoveEmptyEntries);
                for (int i = 0; i < fieldList.Length; i++)
                {
                    m_ddFields.Items.Add(fieldList[i]);
                }

                fieldsInit = true;
            }
            Controls.Add(m_ddFields);

            // Label for operator drop-down
            m_lblOperator = new System.Web.UI.WebControls.Label();
            m_lblOperator.ID = "lblOperator";
            m_lblOperator.Text = "Operators:";
            Controls.Add(m_lblOperator);

            // Operator drop-down initialization
            m_ddOperators = new System.Web.UI.WebControls.DropDownList();
            m_ddOperators.ID = "ddOperators";
            m_ddOperators.Items.Add("=");
            m_ddOperators.Items.Add(">");
            m_ddOperators.Items.Add("<");
            m_ddOperators.Items.Add(">=");
            m_ddOperators.Items.Add("<=");
            m_ddOperators.Items.Add("LIKE");
            Controls.Add(m_ddOperators);

            // Label for sample values drop-down
            m_lblValues = new System.Web.UI.WebControls.Label();
            m_lblValues.ID = "lblValues";
            m_lblValues.Text = "Sample Values:";
            Controls.Add(m_lblValues);

            // Sample values drop-down initialization
            m_ddValues = new System.Web.UI.WebControls.DropDownList();
            m_ddValues.ID = "ddValues";

            if (fieldsInit)
            {
                //get sample values and add to sample values drop-down
                string samples =
                    CustomUtilities.GetSampleValues(m_ddFields.Items[m_ddFields.SelectedIndex].Text,
                    this.ParentEditor);
                char[] delimiter = { ':' };
                string[] sampleList = samples.Split(delimiter, System.StringSplitOptions.RemoveEmptyEntries);

                System.Collections.Specialized.NameValueCollection nvcSampleList =
                    new System.Collections.Specialized.NameValueCollection();

                System.Array.Sort(sampleList);
                for (int i = 0; i < sampleList.Length; i++)
                {
                    m_ddValues.Items.Add(sampleList[i]);
                }
            }
            Controls.Add(m_ddValues);

            // Add to query button 
            m_btnAddToQuery = new System.Web.UI.WebControls.Button();
            m_btnAddToQuery.ID = "btnAddToQuery";
            m_btnAddToQuery.Text = "Add to Query";
            Controls.Add(m_btnAddToQuery);

            // Left parentheses button
            m_btnLtPara = new System.Web.UI.WebControls.Button();
            m_btnLtPara.ID = "btnLtPara";
            m_btnLtPara.Text = "(";
            Controls.Add(m_btnLtPara);

            // Right parentheses button
            m_btnRtPara = new System.Web.UI.WebControls.Button();
            m_btnRtPara.ID = "btnRtPara";
            m_btnRtPara.Text = ")";
            Controls.Add(m_btnRtPara);

            // Percent button
            m_btnPct = new System.Web.UI.WebControls.Button();
            m_btnPct.ID = "btnPct";
            m_btnPct.Text = "%";
            Controls.Add(m_btnPct);

            // Underscore button
            m_btnUnderscore = new System.Web.UI.WebControls.Button();
            m_btnUnderscore.ID = "btnUnderscore";
            m_btnUnderscore.Text = "_";
            Controls.Add(m_btnUnderscore);

            // AND button
            m_btnAnd = new System.Web.UI.WebControls.Button();
            m_btnAnd.ID = "btnAnd";
            m_btnAnd.Text = " AND ";
            Controls.Add(m_btnAnd);

            // OR button
            m_btnOr = new System.Web.UI.WebControls.Button();
            m_btnOr.ID = "btnOr";
            m_btnOr.Text = " OR ";
            Controls.Add(m_btnOr);

            // NOT button
            m_btnNot = new System.Web.UI.WebControls.Button();
            m_btnNot.ID = "btnNot";
            m_btnNot.Text = " NOT ";
            Controls.Add(m_btnNot);

            // Query textbox
            m_txtQuery = new System.Web.UI.WebControls.TextBox();
            m_txtQuery.ID = "txtQuery";
            m_txtQuery.TextMode = System.Web.UI.WebControls.TextBoxMode.MultiLine;
            m_txtQuery.Wrap = true;
            m_txtQuery.Height = (System.Web.UI.WebControls.Unit)80;
            Controls.Add(m_txtQuery);

            // Clear query button
            m_btnClearQuery = new System.Web.UI.WebControls.Button();
            m_btnClearQuery.ID = "btnClearQuery";
            m_btnClearQuery.Text = "Clear";
            Controls.Add(m_btnClearQuery);

            // Execute query button
            m_btnDoQuery = new System.Web.UI.WebControls.Button();
            m_btnDoQuery.ID = "btnDoQuery";
            m_btnDoQuery.Text = "Execute";
            Controls.Add(m_btnDoQuery);
        }

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

            // Register the JavaScript file containing client-side functions used by the panel
            //System.Web.UI.ScriptManager.RegisterClientScriptResource((System.Web.UI.Control)this, this.GetType(),
            //    "CustomEditorTask_CSharp.javascript.QueryBuilderPanel.js");

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

        // Render the panel's child controls
        protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
        {
            if (ParentEditor.MapResource == null)
                return;
            // ==Format the layout of controls on the panel and wire necessary JavaScript

            // Render a table to contain the panel's controls
            writer.AddAttribute(System.Web.UI.HtmlTextWriterAttribute.Cellpadding, "3px");
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.OverflowY, "auto");
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.Width, "90%");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Table);

            // Fields label and drop-down
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.WhiteSpace, "nowrap");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            m_lblFields.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            // Link a JavaScript call to getValues to the onchange event of the Fields drop-down.  this will call
            // getValues when the user selects a new field.
            string jsOnChange = System.String.Format("javascript:getValues('{0}','{1}','{2}')", m_ddFields.ClientID,
                m_ddValues.ClientID, this.CallbackFunctionString.Replace("'", "\\'"));
            m_ddFields.Attributes.Add("onchange", jsOnChange);
            m_ddFields.RenderControl(writer);

            writer.RenderEndTag(); //td
            writer.RenderEndTag(); //tr

            // Operator label and drop-down
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.WhiteSpace, "nowrap");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            m_lblOperator.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_ddOperators.RenderControl(writer);

            writer.RenderEndTag(); //td
            writer.RenderEndTag(); //tr

            // Values label and drop-down
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.WhiteSpace, "nowrap");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            m_lblValues.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_ddValues.RenderControl(writer);

            writer.RenderEndTag(); //td
            writer.RenderEndTag(); // tr

            // Add to query button
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            // link JavaScript call to addToQuery to the onclick event.
            m_btnAddToQuery.OnClientClick = string.Format("addToQuery('{0}', '{1}', '{2}', '{3}')",
                m_ddFields.ClientID, m_ddOperators.ClientID, m_ddValues.ClientID, m_txtQuery.ClientID);
            m_btnAddToQuery.RenderControl(writer);

            writer.RenderEndTag(); // td
            writer.RenderEndTag(); // tr

            // hr to separate sections of panel
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.Width, "80%");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Hr);
            writer.RenderEndTag(); //hr
            writer.RenderEndTag(); //td
            writer.RenderEndTag(); // tr

            // Parentheses, percent, and underscore buttons
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.WhiteSpace, "nowrap");
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            // Format JavaScript to add the text of the button clicked to query.
            string onclick = string.Format("var txt = document.getElementById('{0}');" +
                "txt.innerHTML += this.value; txt.focus();", m_txtQuery.ClientID);

            m_btnLtPara.OnClientClick = onclick;
            m_btnLtPara.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_btnRtPara.OnClientClick = onclick;
            m_btnRtPara.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_btnPct.OnClientClick = onclick;
            m_btnPct.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_btnUnderscore.OnClientClick = onclick;
            m_btnUnderscore.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");

            writer.RenderEndTag(); // td
            writer.RenderEndTag(); // tr

            //AND, OR, and NOT buttons
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.WhiteSpace, "nowrap");
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            m_btnAnd.OnClientClick = onclick;
            m_btnAnd.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_btnOr.OnClientClick = onclick;
            m_btnOr.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            m_btnNot.OnClientClick = onclick;
            m_btnNot.RenderControl(writer);

            writer.RenderEndTag(); //td
            writer.RenderEndTag(); // tr

            // hr to separate sections of panel
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.Width, "80%");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Hr);
            writer.RenderEndTag(); //hr
            writer.RenderEndTag(); //td
            writer.RenderEndTag(); // tr

            // Query textbox
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.Width, "100%");
            m_txtQuery.RenderControl(writer);

            writer.RenderEndTag(); // td
            writer.RenderEndTag(); // tr

            // Clear Query and Execute buttons
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.WhiteSpace, "nowrap");
            writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, "center");
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Td);

            // Link JavaScript to clear query text to fire when the Clear Query button is clicked.
            m_btnClearQuery.OnClientClick = string.Format("document.getElementById('{0}').innerHTML = ''",
                m_txtQuery.ClientID);
            m_btnClearQuery.RenderControl(writer);
            writer.Write("&nbsp;&nbsp;");
            // Link JavaScript call to doQuery to fire when Execute button is clicked.
            m_btnDoQuery.OnClientClick = string.Format("doQuery('{0}','{1}')",
                m_txtQuery.ClientID, this.CallbackFunctionString.Replace("'", "\\'"));
            m_btnDoQuery.RenderControl(writer);

            writer.RenderEndTag(); // td
            writer.RenderEndTag(); // tr
            writer.RenderEndTag(); // table

        }

        #endregion

        #region Web ADF Event Handlers

        void ParentEditor_LayerChanged(ESRI.ArcGIS.Carto.IFeatureLayer featureLayer)
        {
            if (m_ddFields != null)
            {
                // Get object ID field
                this.OIDfieldName = featureLayer.FeatureClass.OIDFieldName;

                string fieldList = CustomUtilities.GetFields(CustomEditorInstance);

                // Format JavaScript to populate the fields drop-down list and add to callback results
                string jsPopulateFieldsDropDown = string.Format("populateDropDown('{0}', '{1}');",
                    m_ddFields.ClientID, fieldList);
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult populateFieldsCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsPopulateFieldsDropDown);
                this.ParentEditor.CallbackResults.Add(populateFieldsCallbackResult);

                // Put the fields contained in the fieldList string into a string array
                char[] separator = { ':' };
                string[] fieldsArray = fieldList.Split(separator, System.StringSplitOptions.RemoveEmptyEntries);

                // Get list of sample values for the first field in the fields list
                string sampleValueList = CustomUtilities.GetSampleValues(fieldsArray[0], this.ParentEditor);
                // Construct JavaScript call to populateDropDown to populate sample values based on the field selected
                string jsPopulateSampleValuesDropDown = string.Format("populateDropDown('{0}', '{1}', '{2}');",
                    m_ddValues.ClientID, sampleValueList, "true");

                // Create new callback result from JavaScript call and add to callback results
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult populateSampleValuesCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsPopulateSampleValuesDropDown);
                this.ParentEditor.CallbackResults.Add(populateSampleValuesCallbackResult);

                // Format JavaScript to clear the query textbox and add to callback results
                string jsClearQueryTextbox = string.Format("document.getElementById('{0}').innerHTML = ''",
                    m_txtQuery.ClientID);
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult clearQueryCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsClearQueryTextbox);
                this.ParentEditor.CallbackResults.Add(clearQueryCallbackResult);
            }
        }

        #endregion;

        #region Callback Handler

        public override string GetCallbackResult()
        {
            // Replace HTML reserved character codes in the callback argument  
            string callbackArgument = this.CallbackEventArgument.Replace("&gt;", ">");
            callbackArgument = callbackArgument.Replace("&lt;", "<");

            // Parse callback arguments using the CallbackUtility included with Web ADF.
            // The CallbackEventArgument is a member variable inherited from 
            // ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl.  During a callback, it 
            // contains the string argument passed to RaiseCallbackEvent().
            System.Collections.Specialized.NameValueCollection nvcCallbackArgs =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(callbackArgument);

            // Get value of EventArg
            string eventArg = nvcCallbackArgs["EventArg"];

            // Check eventArg and take action accordingly
            if (eventArg == "fieldSelected")
            {
                // Get list of sample values
                string sampleValuesList = CustomUtilities.GetSampleValues(nvcCallbackArgs["field"], this.ParentEditor);

                // Construct JavaScript call to populateDropDown to populate sample values based on field selected
                string jsPopulateSampleValuesDropDown = string.Format("populateDropDown('{0}', '{1}', '{2}');",
                    m_ddValues.ClientID, sampleValuesList, "true");

                // Create new callback result with JavaScript call and add to collection
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult populateSampleValuesCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsPopulateSampleValuesDropDown);
                this.CallbackResults.Add(populateSampleValuesCallbackResult);

                //return callback results
                return this.CallbackResults.ToString();
            }
            else if (eventArg == "doQuery")
            {
                // Replace aliases in query string with actual database field names
                string query = this.ReplaceAliases(nvcCallbackArgs["query"]);

                // Exit without returning results if query is null
                if (query == null)
                    return null;

                // Get IDs of features satisfying query
                int[] ids = CustomUtilities.DoQuery(query, this.ParentEditor);
    
                // Get LayerDescription object for currently selected layer
                ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription adfLayerDescription =
                    ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorUtilities.GetLayerDescription(
                    this.ParentEditor.MapFunctionality, this.ParentEditor.SelectedLayerID);

                bool selectionChanged;
                System.Collections.Generic.List<int> selectedIDsArray = 
                    CustomUtilities.UpdateSelection(adfLayerDescription.SelectionFeatures,
                    ids, out selectionChanged, this.ParentEditor.EditorTask);

                // Exit function without returning callback results if selection is unchanged
                if (!selectionChanged)
                    return null;

                // Set selected features on layer currently being edited
                adfLayerDescription.SelectionFeatures = selectedIDsArray.ToArray();

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

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

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

                // Create callback result that calls JavaScript to hide AJAX activity indicator
                string jsHideIndicator = string.Format("showEditorElement(false,'{0}');",
                    "ajaxActivityID");
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult hideActivityIndicatorCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsHideIndicator);
                this.CallbackResults.Add(hideActivityIndicatorCallbackResult);

                // Return callback results
                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

        // Replace field aliases in passed-in string with corresponding actual database field names
        private string ReplaceAliases(string query)
        {            
            // Rebuild query string, replacing aliases with database field names
            string newQuery = query.Substring(0, query.IndexOf("["));
            query = query.Substring(query.IndexOf("["));

            string alias = null;
            string field = null;
            do
            {
                // Get alias from query string
                alias = query.Substring(1, query.IndexOf("]") - 1);
                // Get database field name of alias
                field = CustomUtilities.GetFieldName(alias, CustomEditorInstance);
                if (field == null)
                    return null;

                query = query.Substring(query.IndexOf("]") + 1);

                if (query.IndexOf("[") > -1)
                {                    
                    newQuery += field + query.Substring(0, query.IndexOf("["));
                    query = query.Substring(query.IndexOf("["));
                }
                else
                    newQuery += field + query;
            }
            while (query.IndexOf("]") > -1);

            return newQuery;
        }

        #endregion
    }
}