Common_CustomTasks_VBNet\PostBackTask_VBNet\PostBackTask.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 <Assembly: System.Web.UI.WebResource("CustomSendRequest.js", "text/javascript")> ' Classes encapsulating several methods of invoking and handling asynchronous functionality within a task. Includes ' buttons that (1) issue a partial postback, (2) invoke a custom callback, (3) execute the task via the task framework ' methods, and (4) invoke a callback that is handled in a class other than that of the parent task. Namespace PostBackTask_VBNet <System.Web.UI.ToolboxData("<{0}:PostBackTask runat=""server"" Width=""200px"" Transparency=""35"" " & ControlChars.CrLf & " BackColor=""White"" TitleBarColor=""WhiteSmoke"" TitleBarSeparatorLine=""False"" " & ControlChars.CrLf & " TitleBarHeight=""20px"" BorderColor=""LightSteelBlue"" BorderStyle=""Outset"" BorderWidth=""1px"" " & ControlChars.CrLf & " Font-Names=""Verdana"" Font-Size=""8pt"" ForeColor=""Black""> </{0}:PostBackTask>")> _ Public Class PostBackTask Inherits ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanelTask #Region "Instance Variable Declarations" Private _handleCallbackButton As PostBackTask_VBNet.CustomCallbackButton Private _handlePartialPostBackButton As System.Web.UI.WebControls.Button Private _hiddenHtmlInput As System.Web.UI.HtmlControls.HtmlInputHidden Private _inputControlList As System.Collections.Generic.List(Of String) = New System.Collections.Generic.List(Of String)() Private _taskResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults Private _taskJobID As String #End Region #Region "ASP.NET WebControl Event Handlers - OnLoad, CreateChildControls, OnPreRender, HandlePartialPostBackButton_Click" #Region "Life Cycle Event Handlers - OnLoad, CreateChildControls, OnPreRender" Protected Overrides Overloads Sub OnLoad(ByVal e As System.EventArgs) MyBase.OnLoad(e) ' Pass the task instance to the CustomCallbackButton if the Callback framework ' is being used (i.e. a ScriptManager is not on the page) If Not _handleCallbackButton Is Nothing Then _handleCallbackButton.PostBackTaskInstance = Me End If End Sub Protected Overrides Overloads Sub CreateChildControls() Controls.Clear() MyBase.CreateChildControls() ' If there is no ScriptManager on the page, initialize and add custom callback button to the task. ' Otherwise, add a partial postback button. If Me.ScriptManager Is Nothing Then ' #Region "Custom Callback Button Instantiation" ' Initialize a custom callback button and add it to the task's controls collection _handleCallbackButton = New CustomCallbackButton() _handleCallbackButton.ID = "CallbackButton" _handleCallbackButton.Text = "Handle Callback" _handleCallbackButton.UseSubmitBehavior = False Controls.Add(_handleCallbackButton) ' #End Region Else ' #Region "Partial PostBack Button Instantiation" ' Initialize a button that will trigger a partial postback _handlePartialPostBackButton = New System.Web.UI.WebControls.Button() _handlePartialPostBackButton.ID = "partialPostBackButton" _handlePartialPostBackButton.Text = "Handle Partial PostBack" _handlePartialPostBackButton.UseSubmitBehavior = False ' Wire a handler for the button's click event AddHandler _handlePartialPostBackButton.Click, AddressOf HandlePartialPostBackButton_Click ' Regiester the button as an asyncrhonous postback control so it triggers a partial postback ' when clicked ScriptManager.RegisterAsyncPostBackControl(_handlePartialPostBackButton) ' Add a custom attribute to the button to be handled during the partial postback _handlePartialPostBackButton.Attributes.Add("buttonParameter", "buttonValue") ' Add the button to the task's controls collection Controls.Add(_handlePartialPostBackButton) ' #End Region ' Initialize a hidden element and add it to the task's controls collection _hiddenHtmlInput = New System.Web.UI.HtmlControls.HtmlInputHidden() _hiddenHtmlInput.ID = "hiddenTag" _hiddenHtmlInput.Value = "" Controls.Add(_hiddenHtmlInput) End If ' #Region "Controls for Task Input - TextBox and DropDownList" ' Create a textbox and add it to the task's controls collection Dim textBox As System.Web.UI.WebControls.TextBox = New System.Web.UI.WebControls.TextBox() textBox.ID = "PostBackTaskTextBox" Controls.Add(textBox) ' Add the textbox's ID to the list of input control IDs _inputControlList.Add(textBox.ClientID) ' Create a drop-down list, add items to it, and add it to the task's controls collection Dim dropDownList As System.Web.UI.WebControls.DropDownList = New System.Web.UI.WebControls.DropDownList() dropDownList.ID = "PostBackTaskDropDownList" dropDownList.Items.Add("item 1") dropDownList.Items.Add("item 2") dropDownList.Items.Add("item 3") Controls.Add(dropDownList) ' Add the drop-down list's ID to the list of input controls IDs _inputControlList.Add(dropDownList.ClientID) ' #End Region ' #Region "Button Invoking Asynchronous Request via Custom JavaScript" ' Construct a JavaScript string which, when evaluated, will result in a string of the input ' control IDs, each separated by "~~~" Dim customArgumentBuilder As System.Text.StringBuilder = New System.Text.StringBuilder("'input=' + ") Dim i As Integer = 0 Do While i < _inputControlList.Count customArgumentBuilder.Append(String.Format("ESRI.ADF.System.escapeForCallback($get('{0}').value)", _inputControlList(i))) If i + 1 = _inputControlList.Count Then Exit Do End If customArgumentBuilder.Append(" + '~~~' + ") i += 1 Loop Dim customArguments As String = customArgumentBuilder.ToString() ' Create a button which, when clicked, will initiate an asynchronous request via custom JavaScript Dim customAsyncRequestButton As System.Web.UI.WebControls.Button = New System.Web.UI.WebControls.Button() customAsyncRequestButton.Text = "Send Custom Request" ' Construct JavaScript to initiate an asyncrhonous request via the task's callback function string Dim sendAsyncRequestJavaScript As String = String.Format("sendRequest({0}, ""{1}"");return false;", customArguments, Me.CallbackFunctionString) ' Wire the asynchronous request initiation script to execute when the button is clicked customAsyncRequestButton.OnClientClick = sendAsyncRequestJavaScript ' Add the button to the task's controls collection Controls.Add(customAsyncRequestButton) ' #End Region ' #Region "Button Initiating Task Execution via Web ADF Task Framework" ' Create a button for task execution Dim executeTaskButton As System.Web.UI.WebControls.Button = New System.Web.UI.WebControls.Button() executeTaskButton.Text = "Execute Task" ' Construct JavaScript that calls the Web ADF's executeTask function. This will initiate ' a callback that invokes the task's ExecuteTask method on the server. Dim executeTaskJavaScript As String = String.Format("ESRI.ADF.Tasks.executeTask({0}, ""{1}"");return false;", customArguments, CallbackFunctionString) ' Wire the JavaScript to execute when the button is clicked executeTaskButton.OnClientClick = executeTaskJavaScript ' Add the button to the task's controls colleciton Controls.Add(executeTaskButton) ' #End Region End Sub Protected Overrides Overloads Sub OnPreRender(ByVal e As System.EventArgs) MyBase.OnPreRender(e) ' Register the CustomSendRequest JavaScript file if it has not already been Dim embedTaskScriptKey As String = "embeddedPostBackTaskScript" If (Not Page.ClientScript.IsClientScriptBlockRegistered(embedTaskScriptKey)) Then Dim scriptLocation As String = Page.ClientScript.GetWebResourceUrl(GetType(PostBackTask), "CustomSendRequest.js") Page.ClientScript.RegisterClientScriptInclude(embedTaskScriptKey, scriptLocation) End If ' Check whether the Callback or Partial PostBack framework is being used by checking whether ' a ScriptManager has been included on the page. Then wire client-side logic accordingly. If Me.ScriptManager Is Nothing Then ' #Region "Custom Callback Button Click Handling" ' Get the JavaScript syntax for invoking a callback to the callback button Dim callbackInvocationString As String = Page.ClientScript.GetCallbackEventReference(_handleCallbackButton, "argument", "processCallbackResult", "context") ' Construct JavaScript to create an argument string containing the browser's dimensions ' and execute a callback to pass the argument to the server. Dim callbackJavaScript As String = "" & ControlChars.CrLf & " var argument = 'callbackArgument=' + document.body.clientWidth + '~~~' " & ControlChars.CrLf & " + document.body.clientHeight;" & ControlChars.CrLf & " var context = null;" & ControlChars.CrLf & " {0};" & ControlChars.CrLf & " return;" & ControlChars.CrLf & " " ' Substitute the callback invocation into the callback packaging JavaScript callbackJavaScript = String.Format(callbackJavaScript, callbackInvocationString) ' Wire the script to the callback button's click event _handleCallbackButton.OnClientClick = callbackJavaScript ' #End Region Else ' #Region "Partial PostBack Button Click Handling" ' Get the JavaScript syntax to invoke a postback to the partial postback button Dim postbackInvocationString As String = Page.ClientScript.GetPostBackEventReference(_handlePartialPostBackButton, String.Empty) ' Construct JavaScript to place an argument string in a hidden input element and invoke ' a postback Dim postbackJavaScript As String = "" & ControlChars.CrLf & " $get('{0}').value = 'postbackArgument=' + document.body.clientWidth + '~~~' " & ControlChars.CrLf & " + document.body.clientHeight;" & ControlChars.CrLf & " {1};" & ControlChars.CrLf & " return;" & ControlChars.CrLf & " " ' Substitute the hidden input's client ID and the postback invocation syntax into the ' argument packaging JavaScript postbackJavaScript = String.Format(postbackJavaScript, _hiddenHtmlInput.ClientID, postbackInvocationString) ' Wire the script to the postback button's click event _handlePartialPostBackButton.OnClientClick = postbackJavaScript ' #End Region ' #Region "Partial PostBack Response Handling" ' Create the JavaScript necessary to pass the result of a partial postback to the Web ADF ' JavaScript library's processCallbackResult function Dim scriptKeyCustom As String = "customDataItemScript" If (Not Me.Page.ClientScript.IsStartupScriptRegistered(Me.GetType(), scriptKeyCustom)) Then Dim processDataItemJavaScript As String = "" & ControlChars.CrLf & " " & ControlChars.CrLf & " function onLoadFunction(){{" & ControlChars.CrLf & " Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(AsyncResponseHandler);" & ControlChars.CrLf & " }}" & ControlChars.CrLf & ControlChars.CrLf & " function AsyncResponseHandler(sender, args) {{" & ControlChars.CrLf & " var dataItems = args.get_dataItems();" & ControlChars.CrLf & " if (dataItems['{0}'] != null)" & ControlChars.CrLf & " ESRI.ADF.System.processCallbackResult(dataItems['{0}']);" & ControlChars.CrLf & " }}" & ControlChars.CrLf & ControlChars.CrLf & " Sys.Application.add_init(onLoadFunction);" processDataItemJavaScript = String.Format(processDataItemJavaScript, Me.Page.ClientID) ' Register the script as a startup script so the result handling code is wired during ' applicaton initialization Me.Page.ClientScript.RegisterStartupScript(Me.GetType(), scriptKeyCustom, processDataItemJavaScript, True) End If ' #End Region End If End Sub #End Region #Region "UI Event Handlers - HandlePartialPostBackButton_Click" ' Fires when the Handle Partial PostBack button is clicked Private Sub HandlePartialPostBackButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) ' Get the contents of the hidden input element from the page's request parameters. Since ' the asynchronous request is a partial postback and the input element is a server control, ' we know that this is included in the page request. Dim hiddenValueArguments As String = Page.Request.Params(_hiddenHtmlInput.UniqueID) ' Use the Web ADF's callback parsing utility to split the contents of the hidden element into ' a name-value collection. Note that the parsing utility can be used on any string with the ' format <Param Name 1>=<Value 1>&<Param Name 2>=<Value 2>&... Dim postbackArgsCollection As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(hiddenValueArguments) ' Get the postback argument from the collection Dim postbackArguments As String = postbackArgsCollection("postbackArgument") If (Not String.IsNullOrEmpty(postbackArguments)) Then ' Split the argument string into the input parameters contained within it. Note the ' JavaScript formatting this string was registered in OnPreRender. Dim inputArray As String() = postbackArguments.Split(New String() { "~~~" }, System.StringSplitOptions.None) ' Format a simple task result with the input arguments and display the result in the task ' results container Dim simpleTaskResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult = New ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult("Browser Dimensions", "Width: " & inputArray(0) & "," & "Height: " & inputArray(1)) TaskResultsInstance.DisplayResults(Nothing, Nothing, Nothing, simpleTaskResult) End If ' Zoom the map If Not TaskResultsInstance.MapInstance Is Nothing Then TaskResultsInstance.MapInstance.Zoom(2) End If ' Copy the map's callback results to the task results container's callback results collection TaskResultsInstance.CallbackResults.CopyFrom(TaskResultsInstance.MapInstance.CallbackResults) ' Register the task results container's callback results as a data item. This will pass the ' callback results to the client-side AsyncResponseHandler function that was registered in ' OnPreRender. This function, in turn, will pass the callback results to the Web ADF's ' processCallbackResults function. Me.ScriptManager.RegisterDataItem(Page, TaskResultsInstance.CallbackResults.ToString(), False) End Sub #End Region #End Region #Region "ICallbackEventHandler Member Overrides - GetCallbackResult" ' Fires when the callback framework is being used and an asynchronous request is issued to the task Public Overrides Overloads Function GetCallbackResult() As String ' Use the Web ADF's callback parsing utility to split the argument passed in the callback into ' a collection of name-value pairs Dim callbackArgsCollection As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(Me.CallbackEventArgument) ' Get the value of the EventArg parameter Dim eventArgument As String = callbackArgsCollection("EventArg") ' Get the task input Dim inputString As String = callbackArgsCollection("input") ' Clear the server-side property storing task input Me.Input = Nothing If (Not String.IsNullOrEmpty(inputString)) Then ' Parse the input string into an array of input arguments Dim inputArray As String() = inputString.Split(New String() { "~~~" }, System.StringSplitOptions.None) ' Set the task's server-side input property to reference the input array Me.Input = inputArray End If ' If the event argument is "executeTask," the ExecuteTask method will fire automatically. ' Otherwise, we must explicitly invoke any server side methods we want to execute during the ' callback. If eventArgument = "executeTask" Then ' Get the job ID of the task. This will be used to set up the task result node containing ' the results _taskJobID = callbackArgsCollection("taskJobID") ' Since we are using the task framework, we can simply return the base method's result Return MyBase.GetCallbackResult() ElseIf eventArgument = "customRequest" Then ' Invoke the method we want to execute during the callback. CustomMethod() End If ' Since we have invoked server-side logic outside the scope of the task framework, we explicitly ' return the callback results that have been created during execution of that logic. Return Me.CallbackResults.ToString() End Function #End Region #Region "FloatingPanelTask Overrides - ExecuteTask, GetGISResourceItemDependencies" ' Fires when a callback is initiated by invoking the Web ADF's executeTask function from the client Public Overrides Overloads Sub ExecuteTask() ' Make sure that task input exists If Me.Input Is Nothing Then Return End If ' Cast the task input to a string array Dim inputArray As String() = TryCast(Me.Input, String()) ' Clear out the property storing the task results Me.Results = Nothing ' Create a Task Result Node, set its text to the first input paramter, and initialize ' it via the SetupTaskResultNode method. Dim taskResultNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode() taskResultNode.Text = inputArray(0) TaskResultsInstance.SetupTaskResultNode(Me, _taskJobID, Input, taskResultNode) ' Create a TreeViewPlus Node, set its text to the second input parameter, and add it ' to the node collection of the Task Result Node Dim treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode() treeViewPlusNode.Text = inputArray(1) taskResultNode.Nodes.Add(treeViewPlusNode) ' Zoom the map If Not TaskResultsInstance.MapInstance Is Nothing Then TaskResultsInstance.MapInstance.Zoom(2) End If ' Set the Task Result Node as the result of the task Me.Results = taskResultNode End Sub ' Used by Manager to see if task requires a particular resource item in a resource manager. ' Since this task has no dependencies, the implementation here returns an empty list. Public Overrides Overloads Function GetGISResourceItemDependencies() As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency) Return New System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency)() End Function #End Region #Region "Instance Members - CustomMethod, TaskResultsInstance" #Region "Instance Methods - Custom Method " ' Fires when the Send Custom Request button is clicked Public Sub CustomMethod() ' Make sure task input exists If Me.Input Is Nothing Then Return End If ' Cast the task's input to a string array Dim inputArray As String() = TryCast(Me.Input, String()) ' Create a task result node, set its text as the first item in the input array, and initialze ' a context menu for it Dim taskResultNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode() taskResultNode.Text = inputArray(0) TaskResultsInstance.SetupContextMenu(TaskResultsInstance.RemoveOnlyContextMenu, taskResultNode) ' Initialize a tree view plus node, set its text to the second item in the input array, and ' add it to the nodes collection of the task result node Dim treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode() treeViewPlusNode.Text = inputArray(1) taskResultNode.Nodes.Add(treeViewPlusNode) ' Add the task result node to the nodes collection of the task's task results container TaskResultsInstance.Nodes.Add(taskResultNode) ' Refresh the task results container to apply the addition of the task results node TaskResultsInstance.Refresh() ' Copy the callback results of the task results container to the task's callback results ' collection. GetCallbackResults returns the task's callback results collection to the ' client, so we need to do this so the task results container's callback results are ' processed on the client. Me.CallbackResults.CopyFrom(TaskResultsInstance.CallbackResults) End Sub #End Region #Region "Instance Properties - TaskResultsInstance" ' Convenient access to the first TaskResults control in the Task's TaskResultsContainers collection Friend ReadOnly Property TaskResultsInstance() As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults Get ' Retrieve the TaskResults control if it has not already been If (_taskResults Is Nothing) AndAlso (Not Me.TaskResultsContainers(0) Is Nothing) Then _taskResults = TryCast(ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(Me.TaskResultsContainers(0).Name, Me.Page), ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults) End If Return _taskResults End Get End Property #End Region #End Region End Class ' Class encapsulating an ASP.NET button that handles a callback. Implemented here to show how a task can ' include child controls that process their own callbacks, rather than sending those callback to the parent ' task. Public Class CustomCallbackButton Inherits System.Web.UI.WebControls.Button Implements System.Web.UI.ICallbackEventHandler #Region "Instance Variable Declarations" Private _callbackArg As String Private _postbackTask As PostBackTask_VBNet.PostBackTask #End Region #Region "ICallbackEventHandler Members - RaiseCallbackEvent, GetCallbackResult" ' The first method to execute on the server when a callback is invoked from the client. The string ' containing the callback's parameters is passed to this method. Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent ' Store the callback argument string in an instance variable for reference in GetCallbackResult _callbackArg = eventArgument End Sub ' Fires when a callback string referencing a CustomCallbackButton instance is invoked from the client. ' In the PostBackTask implementation above, the JavaScript needed to do that is wired to a ' CustomCallbackButton instance in the OnPreRender method. Public Function GetCallbackResult() As String Implements System.Web.UI.ICallbackEventHandler.GetCallbackResult ' Use the Web ADF's callback parsing utility to split the callback argument string into a ' collection of name-value pairs. Dim callbackArgsCollection As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(_callbackArg) ' Get the callback argument from the collection Dim callbackArguments As String = callbackArgsCollection("callbackArgument") If (Not String.IsNullOrEmpty(callbackArguments)) Then ' Split the callback argument into a string array Dim inputArray As String() = callbackArguments.Split(New String() { "~~~" }, System.StringSplitOptions.None) ' Create a simple task result containing the input arguments in the array Dim simpleTaskResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult = New ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult("Browser Dimensions", "Width: " & inputArray(0) & "," & "Height: " & inputArray(1)) ' Display the task result in the task results container buddied to the callback button's parent ' PostBackTask. Me.PostBackTaskInstance.TaskResultsInstance.DisplayResults(Nothing, Nothing, Nothing, simpleTaskResult) End If ' Zoom the map buddied to the callback button's parent PostBackTask If Not Me.PostBackTaskInstance.TaskResultsInstance.MapInstance Is Nothing Then Me.PostBackTaskInstance.TaskResultsInstance.MapInstance.Zoom(2) End If ' Copy the map's callback results to the task results container's results collection Me.PostBackTaskInstance.TaskResultsInstance.CallbackResults.CopyFrom(Me.PostBackTaskInstance.TaskResultsInstance.MapInstance.CallbackResults) Return PostBackTaskInstance.TaskResultsInstance.CallbackResults.ToString() End Function #End Region #Region "Instance Properties - PostBackTaskInstance" ' Provides access to the parent task Friend Property PostBackTaskInstance() As PostBackTask Get Return _postbackTask End Get Set _postbackTask = Value End Set End Property #End Region End Class End Namespace