Common Custom EditorTask
Common_CustomEditorTask_VBNet\CustomEditorTask_VBNet\SearchAttributesPanel.vb
' Copyright 2010 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

Imports Microsoft.VisualBasic
Imports System
Namespace CustomEditorTask_VBNet
    <AjaxControlToolkit.ClientScriptResource("CustomEditorTask_VBNet.SearchAttributesPanel", "SearchAttributesPanel.js")> _
    Friend Class SearchAttributesPanel
        Inherits ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorPanel
#Region "Instance Variable Declarations"

        Private m_panelDiv As System.Web.UI.HtmlControls.HtmlGenericControl
        Private m_callbackArgsCollection As System.Collections.Specialized.NameValueCollection

#End Region

#Region "Constructor"

        Public Sub New(ByVal editorTask As ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorTask)
            MyBase.New("Search", editorTask, "searchAttributesPanel")
            ' Add a handler that fires when a new editor layer is selected
            AddHandler ParentEditor.LayerChanged, AddressOf ParentEditor_LayerChanged
        End Sub

#End Region

#Region "WebControl Life Cycle Event Overrides"

        Protected Overrides Sub CreateChildControls()
            MyBase.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)
        End Sub

        Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
            MyBase.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
            Dim editorLayerList As System.Web.UI.WebControls.DropDownList = Nothing
            editorLayerList = TryCast(CustomUtilities.FindControl("editTaskLayerList", TryCast(Me.ParentEditor, System.Web.UI.Control)), System.Web.UI.WebControls.DropDownList)

            ' Get strings containing the numeric and text fields for the current layer
            Dim numericFields As String = CustomUtilities.GetFields(CustomEditorInstance, CustomUtilities.FieldType.Numeric)
            Dim textFields As String = CustomUtilities.GetFields(CustomEditorInstance, CustomUtilities.FieldType.Text)

            ' Get the URL of the activity_indicator image file
            Dim activityIndicatorUrl As String = Me.Page.ClientScript.GetWebResourceUrl(GetType(CustomEditorTask), "CustomEditorTask_CSharp.images.activity_indicator.gif")


            Dim csname1 As String = Me.ClientID & "_init"

            If (Not Page.ClientScript.IsStartupScriptRegistered(Me.GetType(), csname1)) Then
                ' Construct the JavaScript necessary to initialize the client-side search panel with
                ' server-side properties
                Dim scriptBlock As String = "                " & ControlChars.CrLf & "                    function invokeSearchPanelInitialization(){{" & ControlChars.CrLf & "                      initializeSearchPanel('{0}', '{1}', '{2}', '{3}', '{4}', ""{5}"");" & ControlChars.CrLf & "                    }}" & ControlChars.CrLf & "                    Sys.Application.add_init(invokeSearchPanelInitialization);"
                scriptBlock = String.Format(scriptBlock, m_panelDiv.ClientID, editorLayerList.ClientID, numericFields, textFields, activityIndicatorUrl, Me.CallbackFunctionString)

                ' Register the initialization code as a startup script
                Me.Page.ClientScript.RegisterStartupScript(Me.GetType(), Me.ClientID & "_init", scriptBlock, True)
            End If

            ' Initialize the OIDFieldName property
            Me.OIDfieldName = CustomEditorInstance.FeatureLayer.FeatureClass.OIDFieldName
        End Sub

        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            MyBase.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.
            Dim renderSearchPanelCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript("renderSearchPanel();")
            Me.ParentEditor.CallbackResults.Add(renderSearchPanelCallbackResult)
        End Sub

#End Region

#Region "Web ADF Event Handlers"

        ' Fires when the user selects a different layer from the editor task's layers drop-down list
        Private Sub ParentEditor_LayerChanged(ByVal featureLayer As ESRI.ArcGIS.Carto.IFeatureLayer)
            ' Get the object ID field of the newly selected layer
            Me.OIDfieldName = featureLayer.FeatureClass.OIDFieldName

            ' Get the numeric and text fields of the new layer
            Dim numericFields As String = CustomUtilities.GetFields(CustomEditorInstance, CustomUtilities.FieldType.Numeric)
            Dim textFields As String = 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
            Dim jsUpdateFields As String = String.Format("updateSearchPanelFields('{0}', '{1}');", textFields, numericFields)
            Dim updateFieldsCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsUpdateFields)
            Me.ParentEditor.CallbackResults.Add(updateFieldsCallbackResult)
        End Sub

#End Region ';

#Region "Callback Handler"

        Public Overrides Sub RaiseCallbackEvent(ByVal eventArgs As String)
            ' 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" Then
                ' Get the fields selected by the user, with aliases replaced by database field names
                Dim searchFieldsString As String = CustomUtilities.ReplaceAliases(m_callbackArgsCollection("Fields"), CustomEditorInstance)

                ' Parse the fields string into an array
                Dim delimiter() As Char = {";"c}
                Dim searchFieldsArray() As String = searchFieldsString.Split(delimiter, System.StringSplitOptions.RemoveEmptyEntries)

                ' Build a query string based on the selected fields, operator, and search value
                Dim queryString As String = Me.BuildQuery(m_callbackArgsCollection("Operator"), searchFieldsArray, m_callbackArgsCollection("Value"))

                ' Get IDs of features satisfying query
                Dim matchingFeatureIDs() As Integer = CustomUtilities.DoQuery(queryString, Me.ParentEditor)

                ' Get LayerDescription object for currently selected layer
                Dim agsLayerDescription As ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription = ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorUtilities.GetLayerDescription(Me.ParentEditor.MapFunctionality, Me.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
                Dim selectionChanged As Boolean
                Dim selectedIDsList As System.Collections.Generic.List(Of Integer) = CustomUtilities.UpdateSelection(agsLayerDescription.SelectionFeatures, matchingFeatureIDs, selectionChanged, Me.ParentEditor.EditorTask)

                ' Check whether the selected IDs changed
                If selectionChanged Then
                    ' Set selected features on layer currently being edited
                    agsLayerDescription.SelectionFeatures = selectedIDsList.ToArray()

                    ' Get attributes panel and set to edit the attributes of selected features
                    Dim editAttributesPanel As ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditAttributesPanel = TryCast(Me.ParentEditor.AttributesEditor, ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditAttributesPanel)
                    Dim editAttributesPanelResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResultCollection = editAttributesPanel.SetSelectedFeatures(agsLayerDescription.SelectionFeatures)
                    Me.CallbackResults.CopyFrom(editAttributesPanelResults)

                    ' Refresh map
                    ESRI.ArcGIS.ADF.ArcGISServer.Editor.EditorUtilities.RefreshMap(Me.ParentEditor, Me.CallbackResults)

                    ' Refresh toolbars
                    Me.ParentEditor.RefreshToolbars(Me.CallbackResults)
                End If

                ' Create callback result that calls JavaScript to hide AJAX activity indicator
                Dim jsHideIndicator As String = "hideSearchingIndicator();"
                Dim hideIndicatorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsHideIndicator)
                Me.CallbackResults.Add(hideIndicatorCallbackResult)
            Else
                MyBase.RaiseCallbackEvent(eventArgs)
            End If
        End Sub

        Public Overrides Function GetCallbackResult() As String
            ' If the callback was initiated by a search operation, return the panel's callback results
            If m_callbackArgsCollection("EventArg") = "search" Then
                Return Me.CallbackResults.ToString()
            Else
                Return MyBase.GetCallbackResult()
            End If
        End Function

#End Region

#Region "Instance Properties"

        ' Name of object ID field of selected layer
        Protected Property OIDfieldName() As String
            Get
                Return CStr(StateManager.GetProperty("selectedLayerOIDfield"))
            End Get
            Set(ByVal value As String)
                StateManager.SetProperty("selectedLayerOIDfield", value)
            End Set
        End Property

        ' Easy access to the CustomEditor containing the panel instance
        Protected ReadOnly Property CustomEditorInstance() As CustomEditor
            Get
                Return CType(Me.ParentEditor, CustomEditor)
            End Get
        End Property

#End Region

#Region "Instance Methods"

        ' Builds a query string from search parameters
        Private Function BuildQuery(ByVal searchType As String, ByVal searchFields() As String, ByVal searchValue As String) As String
            Dim queryString As String = ""
            Dim searchWords() As String
            ' Check the specified search type
            Select Case 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 i As Integer = 0 To searchWords.Length - 1
                        queryString &= "("
                        For j As Integer = 0 To searchFields.Length - 1
                            queryString &= String.Format("({0} LIKE '%{1}%')", searchFields(j), searchWords(i))
                            If j < searchFields.Length - 1 Then
                                queryString &= " OR "
                            End If
                        Next j
                        queryString &= ")"
                        If i < searchWords.Length - 1 Then
                            queryString &= " AND "
                        End If
                    Next i
                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 i As Integer = 0 To searchWords.Length - 1
                        For j As Integer = 0 To searchFields.Length - 1
                            queryString &= String.Format("({0} LIKE '%{1}%')", searchFields(j), searchWords(i))
                            If (j < searchFields.Length - 1) OrElse (i < searchWords.Length - 1) Then
                                queryString &= " OR "
                            End If
                        Next j
                    Next i
                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 i As Integer = 0 To searchFields.Length - 1
                        queryString &= String.Format("({0} LIKE '%{1}%')", searchFields(i), searchValue)
                        If i < searchFields.Length - 1 Then
                            queryString &= " OR "
                        End If
                    Next i
                Case Else
                    Dim queryOperator As String = Nothing
                    ' For numeric operators, check the search type and initialize a variable 
                    ' storing the corresponding logical operator
                    Select Case searchType
                        Case "Equal to"
                            queryOperator = "="
                        Case "Greater than"
                            queryOperator = ">"
                        Case "Less than"
                            queryOperator = "<"
                    End Select

                    ' 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 i As Integer = 0 To searchFields.Length - 1
                        queryString &= String.Format("({0} {1} {2})", searchFields(i), queryOperator, searchValue)
                        If i < searchFields.Length - 1 Then
                            queryString &= " OR "
                        End If
                    Next i
            End Select

            Return queryString
        End Function

#End Region
    End Class

End Namespace