LANGUAGES: VB.NET
ASP.NET VERSIONS: 2.x
ASP.NET 2.0 Callbacks
Create Rich User Interfaces with Client Side Callbacks, AJAX style.
Page postbacks are by far the most common way to pass data between the client and
server in an ASP.NET application. The simplicity of this technique is appealing,
but the reality is that it’s often inefficient and disorienting for the user. If
a single value on a Web page needs to be updated, why must the entire page (and
the user) suffer through a complete round trip to the server?
Even if performance is not an issue for your application, there are plenty of other
reasons for ditching postbacks whenever possible. For starters, they are just plain
ugly. Even with the speediest servers there are often perceptible flashes and delays
while the browser posts back the page, retrieves a slightly modified version of
the page, then redraws it completely. You may have also noticed that the Back button
doesn’t always operate as the user expects in an ASP.NET application. This is entirely
due to unnecessary postbacks. There are also scrolling problems; when the user has
scrolled the page down and the page posts back, they end up at the top of the page
again — and must scroll back down manually unless the developer goes out of their
way to implement extra code to make it appear as if the page didn’t post back.
Now that it’s been established that postbacks should be avoided whenever possible,
what’s the best way to get around them?
One of the most efficient solutions is to transfer all required data to the browser
and use client-side script to manipulate it dynamically without bothering the server
at all. While this can be a nice solution for developers who are fluent in JavaScript,
large amounts of data frequently prohibit this from being a viable option for dial-up
users and other such outliers. In such cases, however, there is one reasonably
optimal solution ...
Client-side Callbacks
For years it’s been possible to use client-side code to call back to the server
without posting back the entire page, but the complexity has traditionally scared
most developers away. The new client-side callback technology in ASP.NET 2.0 alleviates
many of the headaches and reduces the amount of code necessary to make it happen.
The classic XMLHTTP callback technology is wrapped by ASP.NET 2.0 callbacks, exposing
a relatively simple interface. You may have heard the buzzword “AJAX” recently, which
is a technology nearly identical to XMLHTTP. Certainly in philosophy they are identical,
permitting out of band callbacks so Web pages can update themselves fluidly (see
Figure 1).

Figure 1: ASP.NET 2.0 callbacks wrap
standard XMLHTTP technology, which allows Web pages to update themselves fluidly.
Automatic Callbacks with TreeViews and GridViews
While you can use ASP.NET 2.0 callbacks with virtually any Web control, the
technique is easiest to use with the
new GridView and TreeView controls because the technology is built in. Using client-side
callbacks with these two controls is as simple as setting a couple properties.
The new GridView control can sort and change pages without posting the page back
to the server. The GridView control provides an EnableSortingAndPagingCallback property
that enables its server-side sorting and paging routines to be called out of band;
only the GridView control calls back to the server and refreshes while the rest
of the page stays put. Unfortunately, this functionality is broken in Beta 2 of
ASP.NET 2.0, but it should be fixed by the final release.
(advertisement)
The new TreeView control also implements the client-side callback functionality.
In fact, the ASP.NET 2.0 client-side callback technology was originally developed
specifically for the TreeView control. Luckily for us, the developers had enough
foresight to design it in a generic way so it could be used with other controls,
as well.
TreeView nodes can retrieve their child nodes from the server and expand dynamically,
without requiring a full page refresh. To enable this functionality, the EnableClientSideScript
and PopulateNodesFromClient properties of the TreeView control must be set to True,
and the PopulateOnDemand property of the applicable tree node(s) must also be set
to True. Additionally, the TreeView’s server-side TreeNodePopulate event must be
populated with code that retrieves the node data, places the data into a node structure,
and, finally, adds the node structure to the ChildNodes collection. The node structure
can be created by adding TreeNode objects to the ChildNodes collection of the parent
TreeNode. Figure 2 contains a simplified version of such code;
Figure 3 shows the
page in action.
Figure 2: The TreeView control can
call back to the server to retrieve the data necessary to expand tree nodes, then
place code in the server-side TreeNodePopulate event to add the nodes dynamically.
It can then update its own user interface without requiring the page to post back.

Figure 3: The child nodes in this
TreeView control were retrieved dynamically at run time by client-side code calling
back to the server, thus avoiding a costly page postback.
It’s sure handy to have client-side callback functionality implemented automatically
in the GridView and TreeView controls, but what about all the other Web controls?
With a moderate amount of effort, any Web control can be enhanced with client-side
callback functionality.
The first step is to put the necessary client-side pieces in place. In most cases,
some kind of client-side control will need to trigger the event. It’s important
to not choose a control that causes a postback, because this will negate the client-side
callback. So a button Web control would be a bad choice, but an HTML button works
just fine. The runat=“server” attribute is necessary to attach a client-side event
to the button at run time from server-side code:
<button
runat="server" id="btn">Get Time</button>
The button won’t do anything until it’s wired up with the appropriate code to call
back to the server, receive the result, and display the result. The code in
Figure
4 does exactly that.
Protected
Sub Page_Load(ByVal
sender As Object,
_
ByVal e
As System.EventArgs) Handles
Me.Load
'Define JavaScript
functions that will recieve the result
Dim
sCallBack As String
= _
"function
MyCallBack(result, context){alert(result);}"
Dim
sErrCallBack As String
= _
"function
MyErrCallBack(result,context){alert(result);}"
'Output the
JavaScript functions to the page
Page.ClientScript.RegisterClientScriptBlock(Me.GetType, _
"MyCallBack",
sCallBack, True)
Page.ClientScript.RegisterClientScriptBlock(Me.GetType, _
"MyErrCallBack",
sErrCallBack, True)
'Attach button's
client event to the JavaScript functions
btn.Attributes("OnClick")
= _
Page.ClientScript.GetCallbackEventReference(Me, _
"'Parm'",
"MyCallBack", "null",
"MyErrCallBack", True)
If
Not Page.IsPostBack Then
'Outputs the time the page was initially rendered
Response.Write("Page Rendered at " & Date.Now.ToString)
End
If
End
Sub
Figure 4: This code attaches JavaScript
functions to the client-side button that permits it to call back to the server and
display the result.
The code starts by defining a JavaScript function that will be called with the result
of the server’s response. It also defines a similar JavaScript function that will
be called in the event of an unexpected error. In the second code block these events
are actually output to the page via the new ClientScript property of the Page object.
By collecting related functions into a single place and adding quite a few new functions,
this new property makes managing client scripts easier than it was in ASP.NET 1.x.
In the third code block of Figure 4, the button is wired up to perform the call
back to the server. The GetCallBackEventReference method is used to make this happen.
This function accepts several parameters, as defined in Figure 5.
|
Parameter
|
Type
|
Description
|
|
control
|
System.Web.UI.Control
|
The page or control that’s implementing the callback reference.
|
|
argument
|
String
|
Optionally, a string parameter may be passed from the client to the server-side
function.
|
|
clientCallback
|
String
|
The name of the client-side function that will receive the result of a successful
callback.
|
|
context
|
String
|
The name of the client-side function that will be called before the call back to
the server.
|
|
clientErrorCallback
|
String
|
The name of the client-side function that will be called if there is an error attempting
to call back to the server.
|
|
useAsync
|
Boolean
|
Specifies whether the callback should be synchronous or asynchronous.
|
Figure 5: GetCallBackEventReference parameters.
The final step is for your server-side code to implement the ICallbackEventHandler
interface. Its associated RaiseCallbackEvent is the server-side function that gets
called by the client-side script; Figure 6 shows an example. Before
this function gets called, the page state gets instantiated in a fairly normal way.
That is, ViewState, Session, control values, and all the other forms of server context
are filled so you can use them as you would in any standard page postback. (This
perhaps has slightly more overhead than calling a Web service, but the tradeoff
is that the logic can be kept inside the applicable page, thereby simplifying future
maintenance.) Keep in mind though, that this not quite a normal postback...
page data will not be posted back. The only values that are passed back are
the ones that you explicitly send back via the GetCallbackEventReference method
of Figure 4.
Figure 6: Pages that support client-side
callbacks must implement the ICallbackEventHandler and its associated RaiseCallbackEvent
and GetCallbackResult functions.
The RaiseCallbackEvent function accepts a string parameter and returns a string
parameter. What gets put in these strings is entirely up to you. These parameters
could contain simple text, numeric characters, XML, comma-delimited text, empty
strings, or virtually anything imaginable that can be represented with text.
The GetCallbackResult function is called just before a response is sent back to
the client. This is the place to set the string value that should be returned
back to the client.
(advertisement)
Keep in mind that the user won’t see any visual changes you make to controls from
within this function because HTML is not rendered back to the client. For visual
changes to happen after a client-side callback, they must be done using client-side
code.
Also keep in mind that if you have multiple controls on a page that use callbacks,
they will all call to the same RaiseCallbackEvent. It’s up to you to sort out which
control is calling back and what it wants.
Figure 7 shows the fully functional program in action. The server
time is displayed directly on the page when the page originally renders. When the
user clicks on the button, the page does not post back nor refresh, but it does
call back to the server to retrieve the current server time and display it in a
message box.

Figure 7: The server can be contacted
for updated information at any time without requiring the page to reload. In this
example, the page retrieves the current time from the server.
If you view the source of the page you’ll notice some hidden fields that are implanted
by ASP.NET to help implement the feature. You needn’t worry about these because
it’s all handled automatically. You’ll also notice a script reference that ASP.NET
uses to implement the XMLHTTP operations that are happening behind the scenes. The
reference looks a bit like this:
<script src="/MyWeb/WebResource.axd?d=leT47j4&t=632493"
type="text/javascript"></script>
Conclusion
The new GridView and TreeView controls have first-class callback support. It’s too
bad Microsoft didn’t build such high-quality callback support into the rest of the
Web controls, but at least a callback solution is now feasible if you’re willing
to jump through a few coding hoops.
Client-side callbacks certainly hold a lot of promise for enriching user interfaces
all across the Web. Now users will no longer have to suffer through postbacks that
seem (to them) to happen at arbitrary moments. Back buttons can work intuitively
once again, and users never again need to be disoriented by having their scroll
positions reset. It does take a moderate amount of effort, but your users will love
you for it.
For a more complex (and potentially useful) example of AJAX functionality, read my
WebChat article. Also,
If you're looking for the quickest and easiest way to implement AJAX functionality, I suggest you
investigate the purchase of PowerWEB Live Controls.
If you want in depth coverage of the free ASP.NET AJAX framework
then I suggest you read my book!
The sample code in this article is available
for download.
This original version of
this article (based on beta 2 of ASP.NET) was published in
ASP.NET Pro Magazine.