Total Recall
Implementing ASP.NET 2.0 Script Callback
October 30, 2009
LANGUAGES: ALL
ASP.NETVERSIONS: 2.0
One of the main issues developers faced when they firststarted to develop commercial Web sites was the inherent limitation of using abrowser-based interface. For instance, there were many cases where you wantedto retrieve information from the server after the user had performed someaction, like entering an employee number in a Web page to retrieve the detailsof an employee. To accomplish this, you would post the current page to theserver, retrieve the employee information from the database, and refresh thepage to display the new content.
Although this method of refreshing the whole page is verycommon today, it is inefficient because the Web page refreshes and re-rendersthe entire page of content even if only a small percentage of the page hasactually changed. Refreshing of the page not only results in delays and is awaste of resources, but it also greatly impacts the user experience. However,if the same functionality can be produced without refreshing the page, it cansignificantly enhance the user experience. To accomplish this, you need a wayto execute a piece of code on the server without refreshing the page, which isexactly what ASP.NET 2.0 script callback allows you to do. This article willprovide you with an in-depth understanding of this feature by discussing codeexamples.
The Need for Script Callback
How many times have you been on a Web page and selected avalue in a dropdown box, only to suddenly see the whole page refresh itsdisplay so that it can fetch values for another dropdown box or populate someother control? You can see this demonstrated in many Web sites. As previously mentioned,this refreshing behavior not only results in delays, it also frustrates theusers. In the ASP world, this problem was solved by the use of remote scriptingtechnology. Using remote scripting, you could make a hidden request to theserver and execute a method, which returns only the data that needs to bechanged on the page. The result is a smoother user experience.
With .NET gaining momentum, though, most companies arelooking to migrate their existing ASP applications to ASP.NET to take advantageof the new features provided by the .NET Framework. Even though remotescripting is a powerful feature, ASP.NET 2.0 provides an efficient alternativeto invoke a remote function from a server page without refreshing the browser.This new feature is called ASP.NET 2.0 script callback, and it builds on thefoundation of the XmlHTTP object library. Using the ASP.NET 2.0 script callbackfeature, you can emulate some of the behaviors of a traditional fat-clientapplication in a Web-based application. It can be used to refresh individualcontrols, validate controls, or even to process a form without having to postthe whole page to the server.
The Callback Feature in an ASP.NET Page
In this article, we ll consider an example wherein weretrieve the details of an employee based on the employee number entered in theWeb page. We ll leverage the script callback feature in this scenario anddemonstrate how we can retrieve the employee details without posting the pageback to the server.
Before we look at the example, let s understand the stepsinvolved in using the callback feature:
The client invokes a client-side method thatwill use the callback manager.
The callback manager creates the request to an.aspx page on the server.
The server receives the request and processesthe request by invoking a pre-defined sever-side function namedRaiseCallbackEvent. Then it returns the results to the callback manager in theclient side.
The callback manager receives the serverresponse and calls a callback method located on the client-side. If there is anerror during the server-side processing, the callback manager invokes aseparate callback method.
The client callback method processes the resultsof the server-side call.
Let s first create a new ASP.NET Web site by selecting NewWeb Site from the File menu in Visual Studio 2005. Click on the Browse button inthe New Web Site dialog box. In the Choose Location dialog box, select LocalIIS in the left navigation pane and create a virtual directory namedScriptCallback under MyProjectsaspnetPro, then click Open. Now in the New WebSite dialog box, the location should readhttp://localhost/MyProjects/aspnetPro/ScriptCallback.
After the Web site is created, add a new Web Form by right-clickingon the Web site and selecting Add New Item from the context menu. In the AddNew Item dialog box, select Web Form from the list of templates. Change thename of the file to CallbackExample.aspx and click Add. Modify the code in theCallbackExample.aspx file to read as shown in Listing One.
Let s spilt the implementation shown in Listing One intothree steps:
Implementing the server-side event for callback.
Generating the client-side script for callback.
Implementing the client callback method.
Let s go through each of these steps in detail.
Implementing the Server-side Event for Callback
At the top of the page, we import the required namespacesby using the Import directive. After that, we use the Implements directive toimplement the ICallbackEventHandler interface. This interface has a methodnamed RaiseCallbackEvent that must be implemented to make the callback work:
<%@ Implements Interface=
"System.Web.UI.ICallbackEventHandler"%>
The signature of the RaiseCallbackEvent method is shownhere:
public string RaiseCallbackEvent(string eventArgs)
It takes an argument of type string. You should use thisstring argument if you need to pass values to the server-side method. Insidethe RaiseCallbackEvent method, we first convert the supplied employee ID intoan integer type and then invoke a function named GetEmployeeDetails, passing inthe employee ID as an argument:
int value = Int32.Parse(eventArgs);
return GetEmployeeDetails(value);
As the name suggests, the GetEmployeeDetails method simplyretrieves the details of the employee and returns that information in the formof an XML string. This method starts by retrieving the connection string fromthe web.config file by using the following line:
string connString = System.Configuration.ConfigurationSettings.
ConnectionStrings["northwindConnectionString"].
ConnectionString;
The above line of code retrieves the connection stringfrom the ConnectionStrings section of the web.config file. The connectionstring is stored in the web.config as follows:
connectionString="server=localhost;uid=sa;pwd=thiru;
database=Northwind"/>
After the connection string is retrieved, we then createan instance of the SqlConnection object passing in the connection string as anargument. Then it creates instances of DataSet, SqlDataAdapter, and SqlCommandobjects, passing in the appropriate parameters to their constructors. Then itexecutes the SQL query by invoking the Fill method of the SqlDataAdapterobject. After the query is executed and the results are available in theDataSet object, we then invoke the GetXml method of the DataSet object toreturn the XML representation of the DataSet to the caller. TheRaiseCallbackEvent method receives the returned XML string and simply returnsit to the caller.
Generating the Client-side Script for Callback
Let s move our focus to the Page_Load event of the page.In the beginning of the Page_Load event, we check to see if the browsersupports callback by examining the SupportsCallback property of theHttpBrowserCapabilities object:
if (!Request.Browser.SupportsCallback)
throw newApplicationException
("This browserdoesn't support " +
"Clientcallbacks.");
Then we invoke the Page.GetCallbackEventReference methodto implement the callback in client side. You can use this method to generateclient-side code, which is required to instantiate the asynchronous call toserver:
string src = Page.GetCallbackEventReference(this,"arg",
"DisplayResultsCallback","ctx", "DisplayErrorCallback");
Let s understand the arguments passed to theGetCallbackEventReference method:
this.Control that implements ICallbackEventHandler(Current Page)
arg.String to be passed to server side as argument.
DisplayResultsCallback.Name of the client-side function, which will receive the result from the server-sideevent.
ctx.String to be passed from one client-side function to another client-sidefunction through context parameter.
DisplayErrorCallback.Name of the client-side function that will be called if there is any errorduring this process.
When you execute this page from the browser and view theHTML source code, you ll see that the following callback code is generated becauseof the previously mentioned GetCallbackEventReference method call:
WebForm_DoCallback('__Page',arg,DisplayResultsCallback,
ctx,DisplayErrorCallback)
WebForm_DoCallback is a JavaScript function (introduced inASP.NET 2.0) that in turn invokes the XmlHttp class methods to actually performthe callback.
We then embed the callback code inside a function byconcatenating the callback-generated code with a JavaScript function namedGetEmployeeDetailsUsingPostback using the following code:
string mainSrc = @"function " +
"GetEmployeeDetailsUsingPostback(arg,ctx)" +
"{ " + src +"; }";
Finally, we register the client script block through theRegisterClientScriptBlock method call. Note that in ASP.NET 2.0, thePage.RegisterClientScriptBlock and Page.RegisterStartupScript methods areobsolete. That s why we had to take the help of Page.ClientScript to renderclient-side script to the browser. The Page.ClientScript property returns anobject of type ClientScriptManager, which is used for managing client scripts.
Implementing the Client Callback Method
In the client side, we have a method namedGetEmployeeDetails, which is invoked when the Get Employee Details commandbutton is clicked:
function GetEmployeeDetails()
{
var n =document.forms[0].txtEmployeeID.value;
GetEmployeeDetailsUsingPostback(n,"txtNumber");
}
From within the GetEmployeeDetails method, we invoke amethod named GetEmployeeDetailsUsingPostback and pass in the requiredparameters. Note that the definition of the GetEmployeeDetailsUsingPostback methodis added in the Page_Load event in the server side (through theRegisterClientScriptBlock method call).
After the server-side function is executed, the callbackmanager automatically calls the DisplayResultsCallback method. Let s look atthe code of the DisplayResultsCallback method. In our example, because thevalue returned from the server-side page is an XML string, we load the returnedXML into an XMLDOM parser and then display its contents:
objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
//Load the returned XML string into XMLDOM Object
objXMLDoc.loadXML(strXML);
Then we get a reference to the Employees node by invokingthe selectSingleNode method of the MSXML DOM object:
objEmployee = objXMLDoc.selectSingleNode
("EmployeesRoot").selectSingleNode
("Employees");
If a valid Employees element is returned from the functioncall, we display its contents. We display this information in a div tag bysetting the innerHTML property of the div element to the dynamically constructedHTML.
When you browse to the CallbackExample.aspx file using thebrowser and search for an employee with an employee ID of 1, the output shouldbe somewhat similar to that shown in Figure 1. When you click the Get EmployeeDetails button, you ll notice that the employee information is retrieved fromthe server and displayed in the browser; all without refreshing the page.
Figure 1: Retrieve employeeinformation from the server and display it in the browser without refreshingthe page.
Conclusion
The callback capability in ASP.NET 2.0 is an extremelyuseful feature that can go a long way in increasing the usability of a Webapplication by obviating the need to perform postbacks. You can implementcallbacks to retrieve lookup data, perform validation, execute back-end code,and so on. Callbacks can also play an important role in increasing theperformance of a Web application by fetching only relevant data asynchronously.
This article provides a thorough discussion of the scriptcallback feature in ASP.NET 2.0. This article also provides an example of howto use this new feature to create dynamic, interactive Web applications thatprovide a rich user experience. Although the application we created was simplein functionality, it should provide a solid foundation for understanding how tomigrate existing ASP-based remote scripting applications to ASP.NET 2.0 usingthe script callback feature.
The sample code in this article isavailable for download.
Thiru Thangarathinamworks at Intel Corp. in Chandler, AZ. He specializes in architecting,designing, and developing distributed enterprise class applications using .NET-relatedtechnologies. He has co-authored a number of books in .NET-relatedtechnologies. He has also been a frequent contributor to leading technology-relatedonline publications. He can be reached at mailto:[email protected].
Begin Listing One
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data"%>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Xml"%>
<%@ Implements
Interface="System.Web.UI.ICallbackEventHandler" %>
</p><p> public stringRaiseCallbackEvent(string eventArgs) </p><p> { </p><p> try</p><p> { </p><p> int value = Int32.Parse(eventArgs); </p><p> returnGetEmployeeDetails(value); </p><p> } </p><p> catch (Exception ex) </p><p> { </p><p> throw newApplicationException</p><p> ("An Errorhas occured during the processing " +</p><p> " of yourrequest. Error is :" + ex.Message); </p><p> } </p><p> } </p><p> public stringGetEmployeeDetails(int employeeID) </p><p> { </p><p> string connString =System.Configuration. </p><p> ConfigurationSettings.ConnectionStrings</p><p> ["northwindConnectionString"].ConnectionString; </p><p> SqlConnectionsqlConnection = new</p><p> SqlConnection(connString); </p><p> try</p><p> {</p><p> DataSetemployeeDataset = new</p><p> DataSet("EmployeesRoot"); </p><p> //Pass in the SQLto be executed and the</p><p> //SqlConnectionobject as the argument</p><p> SqlDataAdapter adapter = newSqlDataAdapter();</p><p> SqlCommandcommand = new SqlCommand</p><p> ("SelectEmployeeID,FirstName,LastName," +</p><p> "Title,TitleOfCourtesy,PostalCode from " +</p><p> "Employees Where EmployeeID =" +</p><p> employeeID.ToString(),sqlConnection); </p><p> //Set theSqlCommand object properties</p><p> command.CommandType = CommandType.Text; </p><p> adapter.SelectCommand = command; </p><p> //Fill theDataset with the return value</p><p> adapter.Fill(employeeDataset,"Employees"); </p><p> return employeeDataset.GetXml();</p><p> </p><p> } </p><p> catch (Exception ex) </p><p> { </p><p> throw ex; </p><p> } </p><p> finally</p><p> { </p><p> if(sqlConnection.State == ConnectionState.Open) </p><p> { </p><p> sqlConnection.Close();</p><p> } </p><p> } </p><p> } </p><p> public voidPage_Load(object sender, EventArgs e) </p><p> { </p><p> if(!Request.Browser.SupportsCallback) </p><p> throw newApplicationException</p><p> ("Thisbrowser doesn't support " +</p><p> "Client callbacks."); </p><p> string src =Page.GetCallbackEventReference</p><p> (this,"arg", "DisplayResultsCallback", </p><p> "ctx","DisplayErrorCallback"); </p><p> string mainSrc =@"function " +</p><p> "GetEmployeeDetailsUsingPostback(arg, ctx)" +</p><p> "{ " +src + "; }"; </p><p> Page.ClientScript.RegisterClientScriptBlock</p><p> (this.GetType(),</p><p> "GetEmployeeDetailsUsingPostback",</p> <p> mainSrc, true); </p><p> } </p><p>
functionGetEmployeeDetails()
{
var employeeID =
document.forms[0].txtEmployeeID.value;
GetEmployeeDetailsUsingPostback(employeeID,
"txtNumber");
}
functionDisplayResultsCallback( result, context )
{
varstrXML,objXMLNode,objXMLDoc;
varobjEmployee,strHTML;
objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
//Load the returned XML string into XMLDOMObject
objXMLDoc.loadXML(result);
//Get reference to the Employees Node
objEmployee =objXMLDoc.selectSingleNode
("EmployeesRoot").selectSingleNode
("Employees");
//Check if theemployee reference is valid
strHTML = "";
if (objEmployee != null)
{
//Dynamicallygenerate HTML and append
//the contentsto a string variable
strHTML += "
EmployeeID :" +
objEmployee.selectSingleNode
("EmployeeID").text + "
";
strHTML +="Employee First Name :" +
objEmployee.selectSingleNode
("FirstName").text + "
";
strHTML +="Employee Last Name :" +
objEmployee.selectSingleNode
("LastName").text + "
";
strHTML +="Employee Title :" +
objEmployee.selectSingleNode
("Title").text + "
";
strHTML +="Title Of Courtesy:" +
objEmployee.selectSingleNode
("TitleOfCourtesy").text + "
";
strHTML +="Postal Code:" +
objEmployee.selectSingleNode
("PostalCode").text + "
";
}
else
{
strHTML += "
Employeenot found";
}
strHTML += ""
//Assign the generated HTML into the divtag
divContents.innerHTML = strHTML;
}
functionDisplayErrorCallback(error, context)
{
alert("Employee Query Failed. " + error);
}
>
Employee Details
Enter theEmployee ID:
style="LEFT:149px; TOP: 72px">
value="GetEmployee Details" name="btnGetEmployee"
onclick="GetEmployeeDetails()">
End Listing One
About the Author
You May Also Like