ControlFreak
LANGUAGES:
VB.NET
ASP.NET
VERSIONS: 2.0 (Pre-Release)
ASP.NET 2.0 Callbacks
Create Rich User Interfaces with Client-side Callbacks
By Steve C. Orr
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.
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.
Sub TreeView1_TreeNodePopulate(ByVal sender As Object, _
ByVal e As
System.Web.UI.WebControls.TreeNodeEventArgs) _
Handles
TreeView1.TreeNodePopulate
Select Case
e.Node.Value
Case
"Software"
Dim NewNode As
New TreeNode
NewNode.Text =
"Antivirus"
e.Node.ChildNodes.Add(NewNode)
Case
"Hardware"
Dim NewNode As
New TreeNode
NewNode.Text =
"Keyboards"
e.Node.ChildNodes.Add(NewNode)
End Select
End Sub
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.
Roll Your Own Callbacks
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'",
"null", "MyCallBack", "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.)
Partial Class MyPage
Inherits
System.Web.UI.Page
Implements ICallbackEventHandler
'(Page_Load event listed
in Figure 4)
Public Function
RaiseCallbackEvent(ByVal eventArgument _
As String) As String
Implements _
System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent
Return "The
server time is " & Date.Now.ToString
End Function
End Class
Figure 6: Pages
that support client-side callbacks must implement the ICallbackEventHandler and
its associated RaiseCallbackEvent function.
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.
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>
Future Compatibility
The code snippets in this article were developed with
Visual Studio 2005 Beta 2. It s possible that there could be some syntax
changes before the final release. Additionally, it s been said that ASP.NET 2.0
callbacks will support browsers beyond only Internet Explorer, although that s
not yet the case in Beta 2.
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.
Steve C. Orr is an
MCSD and a Microsoft MVP in ASP.NET. He s been developing software solutions
for leading companies in the Seattle
area for more than a decade. When he s not busy designing software systems or
writing about them, he can often be found loitering at local user groups and
habitually lurking in the ASP.NET newsgroup. Find out more about him at http://SteveOrr.net
or e-mail him at mailto:Steve@Orr.net.