Much has been written over the years regarding the drawbacks of ASP.NET and how MVC/SPAs solve many of these problems, so there’s not much to rehash there. However, if you’re stuck working with older technology, there is a handy tool to make your ASP.NET WebForms site more responsive. Implementing the ICallbackEventHandler interface in your WebForms will help do the following:
- Limit the amount of data that must be retrieved from the server
- Maintain user’s experience by eliminating the need for entire page re-rendering.
To illustrate, lets create a callback that calculates a person’s age.
- Create a new blank ASP.NET Web Application
- Add a Web Form and call it default.aspx
- Add “ICallbackEventHandler” to default page’s code-behind class file.
- There are two methods created. GetCallBackResult and RaiseCallbackEvent.
123456789101112131415161718192021222324using System;using System;using System.Web.UI;namespace Callbacks{public partial class _default : System.Web.UI.Page, ICallbackEventHandler{public string GetCallbackResult(){throw new NotImplementedException();}public void RaiseCallbackEvent(string eventArgument){throw new NotImplementedException();}protected void Page_Load(object sender, EventArgs e){}}} - Register a client script block in the Page_Load method. This script block represents the client side JavaScript that acts as the glue between the client and server.
12345protected void Page_Load(object sender, EventArgs e){Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "CalculateAge", "function CalculateAge(args) { " + Page.ClientScript.GetCallbackEventReference(this, "args","onCalculateAgeSuccess", null, "onCalculateAgeError", false) + ";}", true);} - Press “Play”. When default.aspx loads in the browser, view the source html. Depending on the browser, you’ll see something similar to this:
1234567891011<script type="text/javascript"><span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span>//<![CDATA[ var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } //]]></script><script src="/WebResource.axd?d=pynGkmcFUV13He1Qd6_TZCbjn-UJWTgGu3a0X210S52sb4_WNYgYTp_p2q0SCWnjjOxrxGmuTzJnv3oPFfE6Yw2&t=636398991241906023" type="text/javascript"></script><script type="text/javascript">//<![CDATA[ function CalculateAge(args) { WebForm_DoCallback('__Page',args,onCalculateAgeSuccess,null,onCalculateAgeError,false);}//]]></script>
Notice that the Page.ClientScript.GetCallbackEventReference in our code renders a function call to “WebForm_DoCallback”. We did not create this function, .NET did, and you can see what it looks like by clicking on the script link beginning with “/WebResource.axd”. I won’t pasted the entire contents of that file, but the method signature looks like this:
1function WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)
This function’s arguments align step 5’s server side Page.ClientScript.GetCallbackEventReference method:-
- eventTarget – The server Control that handles the client callback. The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.
- eventArgument – An argument passed from the client script to the server RaiseCallbackEvent method.
- eventCallback – The name of the client event handler that receives the result of the successful server event.
- context – The client script that is evaluated on the client prior to initiating the callback. The result of the script is passed back to the client event handler.
- errorCallback – The name of the client event handler that receives the result when an error occurs in the server event handler.
- useAsync – true to perform the callback asynchronously; false to perform the callback synchronously.
-
- You’ll notice the there are two JavaScript function definitions and a single invocation. onCalcualteAgeSuccess and onCalculateAgeError were registered as the callback event handlers in step 5. They will wait patiently for the server to return something. The Javascript function that is being invoked is the CalculateAge function that we also defined in step 5. It is essentially a wrapper function for the native ASP.NET WebForm_DoCallback function.
- Press play and try both out. On my system, the callback returns 300 KB and the full PostBack returns a whopping 1700 KB, all to retrieve a few characters of data!
Here’s what the code behind looks like now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using System; using System.Web.UI; namespace Callbacks { public partial class _default : System.Web.UI.Page, ICallbackEventHandler { private string returnValue; //This returns "returnValue" public string GetCallbackResult() { return returnValue; } //This code runs directly before GetCallbackResult public void RaiseCallbackEvent(string eventArgument) { var now = DateTime.Now.Year - int.Parse(eventArgument); returnValue = now + " years old."; } protected void Page_Load(object sender, EventArgs e) { lblResult.Text = string.Empty; Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "CalculateAge", "function CalculateAge(args) { " + Page.ClientScript.GetCallbackEventReference(this, "args", "onCalculateAgeSuccess", null, "onCalculateAgeError", false) + ";}", true); } protected void btnFull_Click(object sender, EventArgs e) { lblResult.Text = (DateTime.Now.Year - int.Parse(tbYear.Text)).ToString() + " years old."; } } } |
The two important methods are GetCallbackResult and RaiseCallbackEvent. RaiseCallbackEvent is where the client’s payload enters our code. Immediately after executing this, the GetCallbackResult function returns a string, back to the client.
The *.aspx page looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="Callbacks._default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script> function onCalculateAgeSuccess(args) { document.getElementById("lblResult").innerHTML = args; } function onCalculateAgeError(args) { } </script> </head> <body> <form id="form1" runat="server"> <div> What year were you born?<asp:TextBox runat="server" ID="tbYear"></asp:TextBox> <br /> <button onclick="CalculateAge(document.getElementById('<%= tbYear.ClientID %>').value); return false;">Calculate with Callback</button> <br /> <asp:Button runat="server" id="btnFull" OnClick="btnFull_Click" Text="Calculate with Full Postback" /> <asp:Label runat="server" ID="lblResult"></asp:Label> </div> </form> </body> </html> |
Bookmarked!, I really like it!