Common_CustomTasks_CSharp\PostBackTask_CSharp\PostBackTask.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. // [assembly: System.Web.UI.WebResource("PostBackTask_CSharp.Resources.JavaScript.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_CSharp { [System.Web.UI.ToolboxData(@"<{0}:PostBackTask runat=""server"" Width=""200px"" Transparency=""35"" BackColor=""White"" TitleBarColor=""WhiteSmoke"" TitleBarSeparatorLine=""False"" TitleBarHeight=""20px"" BorderColor=""LightSteelBlue"" BorderStyle=""Outset"" BorderWidth=""1px"" Font-Names=""Verdana"" Font-Size=""8pt"" ForeColor=""Black""> </{0}:PostBackTask>")] public class PostBackTask : ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanelTask { #region Instance Variable Declarations private PostBackTask_CSharp.CustomCallbackButton _handleCallbackButton; private System.Web.UI.WebControls.Button _handlePartialPostBackButton; private System.Web.UI.HtmlControls.HtmlInputHidden _hiddenHtmlInput; private System.Collections.Generic.List<string> _inputControlList = new System.Collections.Generic.List<string>(); private ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults _taskResults; private string _taskJobID; #endregion #region ASP.NET WebControl Event Handlers - OnLoad, CreateChildControls, OnPreRender, HandlePartialPostBackButton_Click #region Life Cycle Event Handlers - OnLoad, CreateChildControls, OnPreRender protected override void OnLoad(System.EventArgs e) { base.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 (_handleCallbackButton != null) _handleCallbackButton.PostBackTaskInstance = this; } protected override void CreateChildControls() { Controls.Clear(); base.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 (this.ScriptManager == null) { #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); #endregion } 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 _handlePartialPostBackButton.Click += new System.EventHandler(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); #endregion // 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); } #region Controls for Task Input - TextBox and DropDownList // Create a textbox and add it to the task's controls collection System.Web.UI.WebControls.TextBox 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 System.Web.UI.WebControls.DropDownList 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); #endregion #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 "~~~" System.Text.StringBuilder customArgumentBuilder = new System.Text.StringBuilder("'input=' + "); for (int i = 0; i < _inputControlList.Count; i++) { customArgumentBuilder.Append(string.Format("ESRI.ADF.System.escapeForCallback($get('{0}').value)", _inputControlList[i])); if (i + 1 == _inputControlList.Count) break; customArgumentBuilder.Append(" + '~~~' + "); } string customArguments = customArgumentBuilder.ToString(); // Create a button which, when clicked, will initiate an asynchronous request via custom JavaScript System.Web.UI.WebControls.Button customAsyncRequestButton = 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 string sendAsyncRequestJavaScript = string.Format("sendRequest({0}, \"{1}\");return false;", customArguments, this.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); #endregion #region Button Initiating Task Execution via Web ADF Task Framework // Create a button for task execution System.Web.UI.WebControls.Button executeTaskButton = 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. string executeTaskJavaScript = 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); #endregion } protected override void OnPreRender(System.EventArgs e) { base.OnPreRender(e); // Register the CustomSendRequest JavaScript file if it has not already been string embedTaskScriptKey = "embeddedPostBackTaskScript"; if (!Page.ClientScript.IsClientScriptBlockRegistered(embedTaskScriptKey)) { string scriptLocation = Page.ClientScript.GetWebResourceUrl(typeof(PostBackTask), "PostBackTask_CSharp.Resources.JavaScript.CustomSendRequest.js"); Page.ClientScript.RegisterClientScriptInclude(embedTaskScriptKey, scriptLocation); } // 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 (this.ScriptManager == null) { #region Custom Callback Button Click Handling // Get the JavaScript syntax for invoking a callback to the callback button string callbackInvocationString = 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. string callbackJavaScript = @" var argument = 'callbackArgument=' + document.body.clientWidth + '~~~' + document.body.clientHeight; var context = null; {0}; return; "; // 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; #endregion } else { #region Partial PostBack Button Click Handling // Get the JavaScript syntax to invoke a postback to the partial postback button string postbackInvocationString = Page.ClientScript.GetPostBackEventReference( _handlePartialPostBackButton, string.Empty); // Construct JavaScript to place an argument string in a hidden input element and invoke // a postback string postbackJavaScript = @" $get('{0}').value = 'postbackArgument=' + document.body.clientWidth + '~~~' + document.body.clientHeight; {1}; return; "; // 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; #endregion #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 string scriptKeyCustom = "customDataItemScript"; if (!this.Page.ClientScript.IsStartupScriptRegistered(GetType(), scriptKeyCustom)) { string processDataItemJavaScript = @" function onLoadFunction(){{ Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(AsyncResponseHandler); }} function AsyncResponseHandler(sender, args) {{ var dataItems = args.get_dataItems(); if (dataItems['{0}'] != null) ESRI.ADF.System.processCallbackResult(dataItems['{0}']); }} Sys.Application.add_init(onLoadFunction);"; processDataItemJavaScript = string.Format(processDataItemJavaScript, this.Page.ClientID); // Register the script as a startup script so the result handling code is wired during // applicaton initialization this.Page.ClientScript.RegisterStartupScript(GetType(), scriptKeyCustom, processDataItemJavaScript, true); } #endregion } } #endregion #region UI Event Handlers - HandlePartialPostBackButton_Click // Fires when the Handle Partial PostBack button is clicked void HandlePartialPostBackButton_Click(object sender, System.EventArgs e) { // 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. string hiddenValueArguments = 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>&... System.Collections.Specialized.NameValueCollection postbackArgsCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection( hiddenValueArguments); // Get the postback argument from the collection string postbackArguments = postbackArgsCollection["postbackArgument"]; if (!string.IsNullOrEmpty(postbackArguments)) { // Split the argument string into the input parameters contained within it. Note the // JavaScript formatting this string was registered in OnPreRender. string[] inputArray = 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 ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult simpleTaskResult = new ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult("Browser Dimensions", "Width: " + inputArray[0] + "," + "Height: " + inputArray[1]); TaskResultsInstance.DisplayResults(null, null, null, simpleTaskResult); } // Zoom the map if (TaskResultsInstance.MapInstance != null) TaskResultsInstance.MapInstance.Zoom(2); // 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. this.ScriptManager.RegisterDataItem(Page, TaskResultsInstance.CallbackResults.ToString(), false); } #endregion #endregion #region ICallbackEventHandler Member Overrides - GetCallbackResult // Fires when the callback framework is being used and an asynchronous request is issued to the task public override string GetCallbackResult() { // Use the Web ADF's callback parsing utility to split the argument passed in the callback into // a collection of name-value pairs System.Collections.Specialized.NameValueCollection callbackArgsCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection( this.CallbackEventArgument); // Get the value of the EventArg parameter string eventArgument = callbackArgsCollection["EventArg"]; // Get the task input string inputString = callbackArgsCollection["input"]; // Clear the server-side property storing task input this.Input = null; if (!string.IsNullOrEmpty(inputString)) { // Parse the input string into an array of input arguments string[] inputArray = inputString.Split(new string[] { "~~~" }, System.StringSplitOptions.None); // Set the task's server-side input property to reference the input array this.Input = inputArray; } // 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") { // 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 base.GetCallbackResult(); } else if (eventArgument == "customRequest") { // Invoke the method we want to execute during the callback. CustomMethod(); } // 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 this.CallbackResults.ToString(); } #endregion #region FloatingPanelTask Overrides - ExecuteTask, GetGISResourceItemDependencies // Fires when a callback is initiated by invoking the Web ADF's executeTask function from the client public override void ExecuteTask() { // Make sure that task input exists if (this.Input == null) return; // Cast the task input to a string array string[] inputArray = this.Input as string[]; // Clear out the property storing the task results this.Results = null; // Create a Task Result Node, set its text to the first input paramter, and initialize // it via the SetupTaskResultNode method. ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode taskResultNode = new ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode(); taskResultNode.Text = inputArray[0]; TaskResultsInstance.SetupTaskResultNode(this, _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 ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode treeViewPlusNode = new ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode(); treeViewPlusNode.Text = inputArray[1]; taskResultNode.Nodes.Add(treeViewPlusNode); // Zoom the map if (TaskResultsInstance.MapInstance != null) TaskResultsInstance.MapInstance.Zoom(2); // Set the Task Result Node as the result of the task this.Results = taskResultNode; } // 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 override System.Collections.Generic.List< ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency> GetGISResourceItemDependencies() { return new System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency>(); } #endregion #region Instance Members - CustomMethod, TaskResultsInstance #region Instance Methods - Custom Method // Fires when the Send Custom Request button is clicked public void CustomMethod() { // Make sure task input exists if (this.Input == null) return; // Cast the task's input to a string array string[] inputArray = this.Input as string[]; // Create a task result node, set its text as the first item in the input array, and initialze // a context menu for it ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode 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 ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode 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. this.CallbackResults.CopyFrom(TaskResultsInstance.CallbackResults); } #endregion #region Instance Properties - TaskResultsInstance // Convenient access to the first TaskResults control in the Task's TaskResultsContainers collection internal ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults TaskResultsInstance { get { // Retrieve the TaskResults control if it has not already been if ((_taskResults == null) && (this.TaskResultsContainers[0] != null)) _taskResults = ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl( this.TaskResultsContainers[0].Name, this.Page) as ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults; return _taskResults; } } #endregion #endregion } // 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 : System.Web.UI.WebControls.Button, System.Web.UI.ICallbackEventHandler { #region Instance Variable Declarations private string _callbackArg; private PostBackTask_CSharp.PostBackTask _postbackTask; #endregion #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 void RaiseCallbackEvent(string eventArgument) { // Store the callback argument string in an instance variable for reference in GetCallbackResult _callbackArg = eventArgument; } // 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 string GetCallbackResult() { // Use the Web ADF's callback parsing utility to split the callback argument string into a // collection of name-value pairs. System.Collections.Specialized.NameValueCollection callbackArgsCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection( _callbackArg); // Get the callback argument from the collection string callbackArguments = callbackArgsCollection["callbackArgument"]; if (!string.IsNullOrEmpty(callbackArguments)) { // Split the callback argument into a string array string[] inputArray = callbackArguments.Split(new string[] { "~~~" }, System.StringSplitOptions.None); // Create a simple task result containing the input arguments in the array ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleTaskResult 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. this.PostBackTaskInstance.TaskResultsInstance.DisplayResults(null, null, null, simpleTaskResult); } // Zoom the map buddied to the callback button's parent PostBackTask if (this.PostBackTaskInstance.TaskResultsInstance.MapInstance != null) this.PostBackTaskInstance.TaskResultsInstance.MapInstance.Zoom(2); // Copy the map's callback results to the task results container's results collection this.PostBackTaskInstance.TaskResultsInstance.CallbackResults.CopyFrom( this.PostBackTaskInstance.TaskResultsInstance.MapInstance.CallbackResults); return PostBackTaskInstance.TaskResultsInstance.CallbackResults.ToString(); } #endregion #region Instance Properties - PostBackTaskInstance // Provides access to the parent task internal PostBackTask PostBackTaskInstance { get { return _postbackTask; } set { _postbackTask = value; } } #endregion } }