ASP.NET 2.0'S Client Callback Feature

xiaoxiao2021-03-06  61

One of the most overlooked features of ASP.NET 2.0, part of Visual Studio 2005 or "Whidbey", is the Client Callback feature. This feature allows you to programmatically call server-side methods through client-side JavaScript code without the need for posting back the page. This article describes how to use the Client callback feature to implement your own callback scenario and introduces the new TreeView control that has this feature built in. The code samples in this article were written using the "Whidbey" version distributed at the Professional Developer's Conference IN LATE 2003.

The need for client callbacks

Due to the nature of the stateless HTTP protocol, every time the Web user requires retrieving of data from the server or wants to invoke some code that needs to be executed on the server-side, he or she has to submit the page first. On post-back, event handlers will execute the code and serve the data back to the user whose browser has to re-render the page. This event model works fine for most of the cases, but imposes a few restrictions that ASP.NET developers have learned to live with. to keep the state of form input controls on the client, the developer either has to work with extensive ViewState information that slows down the page retrieval or has to write some complex programming logic. Secondly, re-rending the page requires some processing time by the Web browser. for small pages this is not a problem, but for a page with heavy DHTML use or simply lots of content, some flickering can occur even if you use SmartNavigation and dial-up users will notice the blue processing Bar Tha t appears at the bottom of the browser. For these reasons, the ability to call server-side methods from client-side code is a request that many Web developers have had for a long time. The good news is that ASP.NET 2.0 has THIS Feature Builtin.how Client Callback Can Be Implement Now

Before we go to ASP.NET 2.0's Client Callback feature, let's take a look at what Web developers currently do to overcome this problem. Calling server-side methods from JavaScript is something that is currently possible using Microsoft's XMLHTTP ActiveX object. This ActiveX object allows you to retrieve XML files over the Internet using the HTTP protocol However, unlike the name implies, you can use this object to issue an HTTP request to any server -. including Classic ASP, regular HTML, or even PHP files - and just . retrieve the raw HTML output Since the XMLHTTP object is pretty much a standard ActiveX object, you can instantiate it using regular JavaScript So, let's take a look at this sample code that retrieves the HTML code from Google's front page:. function RetrieveGoogleFrontPage () {VAR XMLHTTP = New ActiveXObject ("msxml2.xmlhttp.4.0"); XMLHTTP.Open ("get", "http://www.google.com", false); xmlhttp.send (); return xmlhttp.responsext; } From this code Sample, You Can See t hat using the XMLHTTP object is fairly simple. You simply specify a URL to issue the request and retrieve the complete content that is being returned from the Web server. All this is done in JavaScript, so the page on which this code resides is actually not being posted back. Also, notice that the XMLHTTP object returns the complete response text. This means that if you just want to retrieve business data from the server side, you have to write a special page that returns the business data with the unnecessary HTML code That Bloats a regular page.

ASP.NET 2.0'S Client Callback

Now, let's fast forward to ASP.NET 2.0. The new ASP.NET abstracts the use of the XMLHTTP object. Internally the Client Callback feature still uses the XMLHTTP object, but both the Web user as well as the Web developer are shielded from it . The Client Callback feature really consists of two things: the new ICallbackEventHandler interface as well as the new Page.GetCallbackEventReference method The architecture boils down to the following basic steps The Page.GetCallbackEventReference method and its overloads will create JavaScript code snippets that you.. need to place on the client side. These code snippets contain code that sends an HTTP request back to the page (using the XMLHTTP object under the hood). The request is then handled on the server side by a Web control that implements the ICallbackEventHandler interface In Most Cases, That Web Control Is The Page Specific User Controls Or Web Controls That React To The Request, As You Will See Later in This Art icle. Once the request has been handled, the result is then passed back to the client through another JavaScript function whose sole purpose is to react to the result of the request. Let's take a look at this ASP.NET 2.0 code sample that simply retrieves The Server Time and Displays It THROUGH A Regular JavaScript Alert:

<% @ page language = "c #" compilewith = "serverTime.aspx.cs" classname = "asp.servertime_aspx"%> server time </ title> <script language = "javascript"> function GetServerTime () {var message = ''; var context = ''; <% = sCallBackFunctionInvocation%>} function ShowServerTime (timeMessage, context) {alert ( 'The time on the server is: / n' timeMessage);} Function Onerror (Message, Context) {Alert ('An Unhandled Exception Has Occurred: / n' Message);} </ script> </ head> <body> <form id = "mainform" runat = "server"> < Input type = "Button" Value = "Get Server Time" οnclick = "getServertime ();" /> </ form> </ body> </ html></p> <p>using System; using System.Web.UI; namespace ASP {public partial class ServerTime_aspx: ICallbackEventHandler {public string sCallBackFunctionInvocation; void Page_Load (object sender, System.EventArgs e) {sCallBackFunctionInvocation = this.GetCallbackEventReference (this, "message", "ShowServerTime "," context "," OnError ");} public string RaiseCallbackEvent (string eventArgument) {// Uncomment next line to test error handler // throw new ApplicationException (" Some unhandled exception "); return DateTime.Now.ToString () ;}}} The first thing you notice is that the Page implements the ICallbackEventHandler interface This interface really has only one method, namely RaiseCallbackEvent This is the method that is being executed when a request is handled, and as you can see in this.. Example, IT Simply Returns The Current Time on The Server. To create the client code, We Are Making a call to the page.getcallbackeventrefer ence method on page load. As stated before, this method will create the JavaScript code snippet that, when invoked, will initiate the client callback. The GetCallbackEventReference method has several overloads, but in all overloads one has to basically indicate which Web control will react to the request (in this case, it's our own page instance), the JavaScript variable names that contain the parameters specific to this request, and JavaScript functions that are being called when the request returns or errors out. Since this method returns the JavaScript code To INITIATE The Callback, We Simply Wrap That String Inside A JavaScript Function That Invoked on The Button Click. At Run Time, The <</p> <p>% = SCALLBACKFunctionInvocation%> Expression Will Evaluate TO: __ DOCALLBACK ('__ Page'</p> <p>, Message, ShowServerTime, context, OnError) __doCallback is an ASP.NET 2.0 internal JavaScript function that will initiate the HTTP request to call back the server. Notice how the variable and function names we specified in the GetCallbackEventReference method directly translate into this function call The pseudo-code in Figure 1 Illustrates The Event ORDER THAT WILL Take Place WHEN A Callback is initiated. Figure 1:</p> <p>Callback event order Whe the callback initiates, the message variable is the main parameter that will be passed to the server-side RaiseCallbackEvent method. It is important to understand that a JavaScript string value is being passed to a .NET method. Notice how this message has to be of type string, so if you want to pass complex data back to the server, you have to apply some serialization to your data structure to flatten it out as a string. In this example, we do not need to pass any information to the server, so we just initialize the message variable as an empty string. The same applies to the return value of this method. Again, notice that a .NET string is being returned to a JavaScript method as a string value. The two JavaScript functions ShowServerTime and OnError are self-explanatory. These are the functions that will be called from the server upon finishing the client callback request. ShowServerTime's first parameter will hold the value of whatever is returned from the Ra iseCallbackEvent method (which in our case is just the server time). OnError's first parameter will hold the value of the Message property of whatever unhandled exception has occurred on the server side. The context variable is an interesting variable. Although we pass this variable to the function call, it is not passed to the server-side method (as we know that the RaiseCallbackEvent method has only one parameter). Instead, the context variable is cached on the browser throughout the entire callback and then passed as the second parameter to THE RETURNING JAVAScript functions. this will allow us to ide Identify the context of this entire callback.</p> <p>Imagine a scenario, where you are initiating several requests to the server that really serve two separate events or a case where you have several concurrent callbacks for the same event. You can not be guaranteed that the returning JavaScript methods are called in the same order in which the requests were initiated in, so the context variable allows you to mark each request with a unique value and act upon this value as it is being returned to the JavaScript functions. If you run this example, you will see when clicking on the Show Server Time button, that the entire event is handled in the background without the page being refreshed. Also notice that the client callback is handled asynchronously, so even if the server-side method might take several seconds or minutes to complete, the client browser's user interface Is Not Being Blocked. of Course, IF The Web User Navigates TO A New Page, The JavaScript Functions Won't Be Invoked Anymore, But The RaiseCallBackevent Will Finish To ITS C OMPLETION.THE New TreeView Control</p> <p>At the beginning of the article, I mentioned that any Web control can implement the ICallbackEventHandler interface. In our previous example, I have reused the same page to implement this interface. However, you can have your own user controls or even server controls implement this interface as well. In fact, the new TreeView control shipped with ASP.NET 2.0 implements this interface. As the name implies, this control renders a tree view to display hierarchical data on the client side. It's a very rich control that can databind to a static XML file on the server. you can also create the tree on-the-fly using the same similar syntax as we know it from Windows Forms world. Now what's cool about this control is that when you set the PopulateNodesFromClient and EnableClientScript property to True, you can allow the treeview to populate it, this is achieved us, this is achieved us Ut even the user of this interface has been complesetly abstract for the development.putting it all together: the client-side explorer</p> <p>Let's put this all together in an example where I use the TreeView control to create a Windows Explorer-like user interface for the Web. This example allows a Web client to navigate through the entire hard drive of the Web server and obtain the properties of any . file - all this without the need to post back the page or pre-cache the entire data on the client side FIGURE 2 shows what are we are going to build FIGURE 2: Client Side Explorer The page consists of an address bar TextBox at. the top, the folder TreeView on the left, and a regular file-listing ListBox on the right. Expanding the tree nodes will populate the children nodes on demand. Selecting a tree node will fill the list of files on the right and clicking on any File on The Right Will Display ITS File Properties. Here's The code:</p> <p><% @ page language = "c #" compilewith = "default.aspx.cs" classname = "asp.default_aspx" enableViewState = "true" enablesationState = "false"%> <html> <head runat = "server"> <title > Client Side Explorer </ title> <script language = "javascript"> function OnFolderClick (sFolderPath) {var sMessage = sFolderPath; // create context variable var oContext = new Object (); oContext.CommandName = "GetFileListing"; oContext. FolderName = sFolderPath; <% = sCallBackFunctionInvocation%>} function OnFileClick (sFileName) {var oCurrentFolderBox = document.forms [0] .CurrentFolder; var sMessage = oCurrentFolderBox.value "//" sFileName; // create context variable var oContext = new object (); ocontext.commandName = "getfileinformation"; ocontext.FileName = sfilename; <% = scallbac kFunctionInvocation%>} function CallBackReturnFunction (sReturnValue, oContext) {if (oContext.CommandName == 'GetFileListing') {// process results for folder listing var oFileBox = document.forms [0] .FileListing; var oCurrentFolderBox = document.forms [ 0] .currentfolder; // set current folder path occupicalfolderbox.value = OCONText.Foldername;</p> <p>// deserialize file string var Afiles = SRETURNVALUE.SPLIT ('|'); // Clear current file listing while (ofilebox.Length> 0) OfileBox.Options [0] = null; // create new file listing for (i = 0; I <Afiles.Length; i ) OfileBox.Options [ofileBox.Length] = New Option (Afiles [i]);} else if (OconText.commandName == 'getfileinformation') {// Process Result for File Information Alert ( 'File Information for "' oContext.FileName '" / n / n' sReturnValue);} else alert ( '. Invalid context');} function OnCallBackError (exception, context) {alert ( 'Unhandled exception occurred: / n ' exception;} </ script> </ head> <body> <form runat = "server"> <h3> Client Side Explorer </ h3> <Ta BLE style = "align = center; background-color: #ccccccc; width: 90%; Height: 90%; Border-style: solId;"> <tr style = "Height: 20;"> <TD colspan = "2 "> <Input type =" text "id =" currentfolder "style =" width: 100% "name =" currentfolder "/> </ td> </ tr> <tr> <td style =" Width: 200; Background -Color: White; Vertical-Align: TOP>></p> <p><asp: panel id = "TreePanel" runat = "server" scrollbars = auto "style =" width: 100%; Height: 100%; "BorderStyle =" INSET "> <ask: TreeView id =" foldertree "runat = "server" font-names = "Tahoma" font-size = "8pt" ImageSet = "XP_Explorer" NodeIndent = "15" ShowLines = "true" PathSeparator = "/" PopulateNodesFromClient = "true" EnableClientScript = "true" /> < / asp: panel> </ td> <TD> <select id = "filelisting" size = "2" style = "width: 100%; Height: 100%" onclick = "onfileclick (this.Options [this.selected" .text "/> </ td> </ tr> </ table> </ Form> </ body> </ html></p> <p>Looking at the ASPX code above, you will see that I have placed the TreeView inside a single Panel control for the sole purpose of making use of the panel's new scrollbars property. I declaratively use some of the TreeView's properties to control the appearance of the TreeView to resemble that of the usual Windows Explorer. Notice that we are setting the PopulateNodesFromClient property to True to indicate that the TreeView should issue a client callback to the server each time a node is expanded, and since this requires some client-side code, we also need to enable the EnableClientScript property to True On the server side, we need write the code to handle the population of tree nodes On the initial load of the page, I am adding the root node that contains the c:.. / root directory as its value. Since I want all TreeNodes to populate on demand, I need to set the PopulateOnDemand Property to True. In addition, I am setting the NavigateUrl property to call the OnFolderClick JavaScript fu NCTION:</p> <p>using System; using System.Web.UI; using System.IO; using System.Text; using System.Web.UI.WebControls; namespace ASP {public partial class default_aspx: ICallbackEventHandler {public string sCallBackFunctionInvocation; void Page_Load (object sender, System .EventArgs e) {// create callback code sCallBackFunctionInvocation = this.GetCallbackEventReference (this, "sMessage", "CallBackReturnFunction", "oContext", "OnCallBackError") ";"; // hook into tree node population event FolderTree.TreeNodePopulate = new TreenodeEventHandler (Foldertree_treenodepopulate); // for the intial load, we need to add the root-node if (! page.ispostback) {Treenode rootnode = new Treenode (@ "c: /", "c:"); Rootnode.navigate: onfolderclick ('c: //'); "; rootnode.populateOndemand = true; foldertree.nodes.add (rootnode);}} private void foldertree_treenodepepep opulate (object sender, TreeNodeEventArgs e) {// obtain the current Directory DirectoryInfo currentDirectory = new DirectoryInfo (e.Node.ValuePath FolderTree.PathSeparator); if (currentDirectory.Exists) {// go through each sub directory foreach (DirectoryInfo subDirectory in currentDirectory.GetDirectories ()) {// and create a new tree node for each of them TreeNode subNode = new TreeNode (subDirectory.Name, subDirectory.Name); subNode.NavigateUrl = "javascript: OnFolderClick ( '" </p> <p>Subdirectory.FullName.Replace ("//", ") "); "; subnode.populateOndemand = true; // add new sub-node to the current node e.node.childNodes.add (subnode);} }} public string RaiseCallbackEvent (string eventArgument) {StringBuilder sReturnValue = new StringBuilder (); // if a directory is requested if (Directory.Exists (eventArgument)) {// create a pipe-delimited list of sub directories foreach (string sFile In Directory.Getfiles (Eventargument)) SreturnValue.Append (SFile) ""); Return SretrnValue.toString (). Trimend ('|');} else if (file.exists (evenetargument)) { // if a file is requested fileinfo ofile = new fileinfo (Eventargument); // Create Message for File Properties SreteValue.Append ("File Size: / T" Ofile.Length / 1024 "KB" Environment.n ewLine); sReturnValue.Append ( "Creation Time: / t" oFile.CreationTime Environment.NewLine); sReturnValue.Append ( "Access Time: / t" oFile.LastAccessTime Environment.NewLine); sReturnValue.Append ( " Write Time: / t " oFile.LastWriteTime Environment.NewLine); // return return value return sReturnValue.ToString ();} else throw new System.ApplicationException (" Invalid message ( " eventArgument ") was passed to server ");</p> <p>}}} I also need to create an event handler for the TreeNodePopulate event of the tree view. In the event handler, I simply obtain the full path of the TreeNode (the Node.ValuePath property of a TreeNode will concatenate all values ​​of each TreeNode along its path into a single string), and then use the DirectoryInfo.GetDirectories method to iterate over all the subdirectories of the TreeNode that is being expanded. For each subdirectory, I am creating a new TreeNode and adding it to the TreeView. Please note that this method is being called from the client side when a tree node is being expanded and that all the internal plumbing using Page.GetCallbackEventReference and RaiseCallbackEvent is already abstracted for us. What's now left to do is to create the handlers to populate the ListBox with Files WHEN A DIRECTIES WHEN A SINGLE FILE IS CLICKED. THESE ARE TW Separate Events, SO i Have To Use The Client Callback Architecture As Described in Thi s article in a more tricky way. First, I use the Page.GetCallbackEventReference method to generate the JavaScript that will initiate the client callback, but since the RaiseCallbackEvent method will handle events for both type of requests, I take a look at the string argument being passed and return the file properties if the argument resembles a filename or return a pipe-delimited list of filenames if the argument resembles a directory. This analysis of the argument suffices for this example, but as mentioned before, in more complex scenarios where you Want to Pass Several Arguments, You Have To Combine The Into As Single String Using Delimited Lists, XML, or Your OWN Serialization Technique.</p> <p>On the client side, the OnFolderClick JavaScript method will be called every time the user clicks on a node in the folder tree. This has been achieved, by adding this JavaScript function call to every TreeNode that I have created. On the other hand, the OnFileClick JavaScript method will be called when a filename is selected from the ListBox control. Since only the filename is being passed to this JavaScript function, I obtain the full path of the current directory from the address bar above. Both JavaScript functions initiate the callback through the code that was generated by the GetCallbackEventReference, so both functions wrap around the call to the <% = sCallBackFunctionInvocation%> expression. Now we have a case where it is necessary to use the context variable to differentiate between these two types of events. Therefore , in Both Function Calls I Instantiate a Generic JavaScript Object with a commandname Property That i set to a hard-code string and a foldername or filename ProPE rty to hold the appropriate values. Notice how I am passing that entire generic object as the context variable, so on the CallBackReturnFunction method (my JavaScript event handler for the callback return), I will receive this object with its existing properties. I now only have to inspect the CommandName property to find out what type of event this is and react to it accordingly. For a folder listing, I first update the address bar TextBox above and deserialize the pipe-delimited list of filenames and fill those into the ListBox. In The Event of a FileName Selection, I Simply Display The File Properties Through A Regular JavaScript Alert Box.conclusion</p></div><div class="text-center mt-3 text-grey"> 转载请注明原文地址:https://www.9cbs.com/read-113290.html</div><div class="plugin d-flex justify-content-center mt-3"></div><hr><div class="row"><div class="col-lg-12 text-muted mt-2"><i class="icon-tags mr-2"></i><span class="badge border border-secondary mr-2"><h2 class="h6 mb-0 small"><a class="text-secondary" href="tag-2.html">9cbs</a></h2></span></div></div></div></div><div class="card card-postlist border-white shadow"><div class="card-body"><div class="card-title"><div class="d-flex justify-content-between"><div><b>New Post</b>(<span class="posts">0</span>) </div><div></div></div></div><ul class="postlist list-unstyled"> </ul></div></div><div class="d-none threadlist"><input type="checkbox" name="modtid" value="113290" checked /></div></div></div></div></div><footer class="text-muted small bg-dark py-4 mt-3" id="footer"><div class="container"><div class="row"><div class="col">CopyRight © 2020 All Rights Reserved </div><div class="col text-right">Processed: <b>0.039</b>, SQL: <b>9</b></div></div></div></footer><script src="./lang/en-us/lang.js?2.2.0"></script><script src="view/js/jquery.min.js?2.2.0"></script><script src="view/js/popper.min.js?2.2.0"></script><script src="view/js/bootstrap.min.js?2.2.0"></script><script src="view/js/xiuno.js?2.2.0"></script><script src="view/js/bootstrap-plugin.js?2.2.0"></script><script src="view/js/async.min.js?2.2.0"></script><script src="view/js/form.js?2.2.0"></script><script> var debug = DEBUG = 0; var url_rewrite_on = 1; var url_path = './'; var forumarr = {"1":"Tech"}; var fid = 1; var uid = 0; var gid = 0; xn.options.water_image_url = 'view/img/water-small.png'; </script><script src="view/js/wellcms.js?2.2.0"></script><a class="scroll-to-top rounded" href="javascript:void(0);"><i class="icon-angle-up"></i></a><a class="scroll-to-bottom rounded" href="javascript:void(0);" style="display: inline;"><i class="icon-angle-down"></i></a></body></html><script> var forum_url = 'list-1.html'; var safe_token = 'Va1uuzuabSi5UJRNwBoXcR6ZikYoRQW6mvJ_2FkIEnvv7NRSE0DxcbABgUjm2vy07PukgTVkTe8fY49153WoMlqA_3D_3D'; var body = $('body'); body.on('submit', '#form', function() { var jthis = $(this); var jsubmit = jthis.find('#submit'); jthis.reset(); jsubmit.button('loading'); var postdata = jthis.serializeObject(); $.xpost(jthis.attr('action'), postdata, function(code, message) { if(code == 0) { location.reload(); } else { $.alert(message); jsubmit.button('reset'); } }); return false; }); function resize_image() { var jmessagelist = $('div.message'); var first_width = jmessagelist.width(); jmessagelist.each(function() { var jdiv = $(this); var maxwidth = jdiv.attr('isfirst') ? first_width : jdiv.width(); var jmessage_width = Math.min(jdiv.width(), maxwidth); jdiv.find('img, embed, iframe, video').each(function() { var jimg = $(this); var img_width = this.org_width; var img_height = this.org_height; if(!img_width) { var img_width = jimg.attr('width'); var img_height = jimg.attr('height'); this.org_width = img_width; this.org_height = img_height; } if(img_width > jmessage_width) { if(this.tagName == 'IMG') { jimg.width(jmessage_width); jimg.css('height', 'auto'); jimg.css('cursor', 'pointer'); jimg.on('click', function() { }); } else { jimg.width(jmessage_width); var height = (img_height / img_width) * jimg.width(); jimg.height(height); } } }); }); } function resize_table() { $('div.message').each(function() { var jdiv = $(this); jdiv.find('table').addClass('table').wrap('<div class="table-responsive"></div>'); }); } $(function() { resize_image(); resize_table(); $(window).on('resize', resize_image); }); var jmessage = $('#message'); jmessage.on('focus', function() {if(jmessage.t) { clearTimeout(jmessage.t); jmessage.t = null; } jmessage.css('height', '6rem'); }); jmessage.on('blur', function() {jmessage.t = setTimeout(function() { jmessage.css('height', '2.5rem');}, 1000); }); $('#nav li[data-active="fid-1"]').addClass('active'); </script>