Although ASP.NET is a server-side programming environment, there are times when client-side script is required. This is especially true when dealing with smart browsers, such as Internet Explorer, where programming client-side can enhance the user experience. To improve this interaction ASP.NET 2.0 introduces several new features, including the ability to set the focus of a control, the concept of a default button, and some advanced client-to-server processing. Form FocusTo set the client-side focus, there are two new methods and an attribute on the form. Each control has a Focus method: btn1.SetFocus() The Page has a SetFocus method: Page.SetFocus(btn1) Finally, the form has an additional attribute ( DefaultButton ) to set the default button: <form runat="server" DefaultButton="btn2"> These can be used as shown in Listing 9.5. Listing 9.5 Using the Focus and SetFocus Methods<script runat="server"> Sub btn1_click(sender As Object, E As EventArgs) btn3.Focus() End Sub Sub btn2_click(sender As Object, E As EventArgs) ... End Sub Sub btn3_click(sender As Object, E As EventArgs) Page.SetFocus(btn1) End Sub </script> <form runat="server" DefaultButton="btn2"> <asp:Button id="btn1" Text="Button 1" onClick="btn1_click" runat="server" /> <asp:Button id="btn2" Text="Button 2" onClick="btn2_click" runat="server" /> <asp:Button id="btn3" Text="Button 3" onClick="btn3_click" runat="server" /> </form> The default focus is applied only when no other focus methods are used. Client Click EventsFor server controls, the OnClick event refers to a server event. To hook a client-side event procedure into the click of a server button requires adding an attribute. To make this easier, ASP.NET 2.0 has the OnClientClick method: <asp:Button runat="server" OnClientClick="ClientScript" /> The onClientClick event is supported by the Button , ImageButton , and LinkButton controls. Registering Script BlocksProgrammatically adding client-side script to a page is achieved with RegisterClientScriptBlock and RegisterStartupScript , which both take a key and the script. This causes problems because it's not possible to use the same key for both a start-up script and another script block. To preserve compatibility, these methods still work, but a new ClientScriptManager , accessible from the ClientScript property of the page, provides enhanced features. For example: Sub Page_Load(Sender As Object, E As EventArgs) Dim Script As String = "alert('Morning everyone');" Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _ "FormValidation", Script) End Sub An additional parameter has also been added to allow the insertion of the <script> tags. For example: Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _ "FormValidation", Script, True) Script IncludesIn version 1.x of ASP.NET, to include a reference to an included script you had to do the following: Sub Page_Load(Sender As Object, E As EventArgs) Dim URL As String = Page.ResolveUrl("~/Scripts/MyScript.js") Dim Script As String = "<script language='javascript'" & _ " src='" & URL & "'></script>" Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _ "FormValidation", Script) End Sub With version 2.0 there is now a RegisterClientScriptInclude method to make this easier. For example: Sub Page_Load(Sender As Object, E As EventArgs) Dim URL As String = "Scripts/MyScript.js" Page.ClientScript.RegisterClientScriptInclude(Me.GetType(), _ "FormValidation", URL) End Sub This will generate the following: <script language="javascript" src="Scripts/MyScript.js"></script> Client CallbacksClient callbacks allow client-side script to call server-side events asynchronously, without causing a postback. This opens up a whole avenue for creating responsive interfaces, where data can be fetched or commands can run in the background. ASP.NET 2.0 introduces the CallBackManager , which acts as an intermediary between client code and server code. It is responsible for accepting requests from the client, firing the appropriate server event, and then passing the results back to the client, as shown in Figure 9.5. Figure 9.5. Client callback architecture Several things are required for this process to work.
We'll look at the individual implementation details first, then look at a simple example. Implementing the Callback Event HandlerIf a page or control wishes to receive callback events, it must implement the ICallbackEventHandler interface. For a control developer this is simply a matter of adding the Implements keyword to the class definition: Public Class MyControl Inherits WebControl Implements ICallbackEventHandler End Class For a Web page the Implements directive is used: <%@ Page Language="VB" %> <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> Implementing the interface is simple because it has only one method, which is the method that will be called from the client. Its signature is: Function RaiseCallbackEvent(eventArgument As String) As String The single argument will contain data from the client, and the return value is returned back to the client. For example: Function MyCallbackEvent(eventArgument As String) As String _ Implements ICallbackEventHandler.RaiseCallbackEvent Return "The server saw " & eventArgument End Function Client-Side ScriptOn the client, there needs to be a minimum of two scriptsone to call the server, and one to act as the callback from the server. The latter of these must match the following signature: <script> function MyCallback (result, context) { } </script> The return value from the server RaiseCallbackEvent will be passed into the above function in the result parameter, and context is passed from the context parameter of GetCallbackEventReference , which is described in the next subsection. There can also be a third script, of the same signature as the MyCallback script, which is run if an error occurs during the callback procedure. Generating the Client Callback CodeFor the client script to call back to the server, the client needs to know the control implementing the ICallbackEventHandler interface. For this the GetCallbackEventReference method is used (in much the same way that GetPostbackEventReference is used for controls responding to postbacks). The GetCallbackEventReference method is overloaded, having the signatures shown in Listing 9.6. Listing 9.6 GetCallbackEventReference SyntaxPublic Function GetCallbackEventReference( control As Control, argument As String, clientCallback As String, context As String) As String Public Function GetCallbackEventReference( control As Control, argument As String, clientCallback As String, context As String, clientErrorCallback As String) As String Public Function GetCallbackEventReference( target As String, argument As String, clientCallback As String, context As String, clientErrorCallback As String) As String Table 9.5. Parameters for GetCallbackEventReference
Parameters for these signatures are shown in Table 9.5. Listing 9.7 shows how to get the reference and register it as a script block. Listing 9.7 Generating the Client CallbackDim refscr As String = _ Page.GetCallbackEventReference(Me, "arg", "MyCallBackHandler", _ "ctx", "null") Dim scr As String = _ "function CallTheServerCallBack(arg, ctx) {" _ & refscr & "; }" Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _ "CallTheServerCallBack", scr, True) The first line constructs the actual line of client script that will generate the callback. For example, if this is run from the Page_Load event of an ASP.NET page, it will generate: __doCallback('__Page',arg,MyCallBackHandler,ctx,null) These parameters are:
This __ doCallback function needs to be hooked into a client-side event to trigger the callback. This can be either directly triggered (such as from the onClick event of a button) or wrapped in another function, which is what the following line does: Dim scr As String = _ "function CallTheServerCallBack(arg, ctx) {" _ & refscr & "; }" This simply turns the callback routine into: function CallTheServerCallBack(arg, ctx) { __doCallback('__Page',arg,MyCallBackHandler,ctx,null) } The final part of the required action is to register this block of client script: Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _ "CallTheServerCallBack", scr, True) At this stage you now have a client script function that will perform the callback. The server implementation of RaiseCallbackEvent will process the argument and return its data back to MyCallBackHandler . Figure 9.6 on the next page shows a new copy of the client callback architecture diagram to make things clearer. Figure 9.6. Client callback architecture with code Client Callbacks in ActionSince the code for this is confusing without a good example, let's look at the entire code for a page called ClientSideCallback.aspx (see Figure 9.7). Figure 9.7. ClientSideCallback.aspx Entering a value into the top text box and pressing the button performs a client callbackin this case simulating the validation of a zip code. The results are displayed in the lower text area (see Figure 9.8). Figure 9.8. ClientSideCallback.aspx in action The code for this is shown in Listing 9.8. Listing 9.8 Using Client Callbacks<%@ page language="VB" %> <%@ implements interface="System.Web.UI.ICallbackEventHandler" %> <script runat="server"> Function ClientCallBackFunction(EventArgument As string) As String _ Implements ICallbackEventHandler.RaiseCallbackEvent Return EventArgument & " has been validated" End Function Sub Page_Load(sender As object, e As EventArgs) Dim refscr As String = Page.GetCallbackEventReference(Me, "arg", _ "MyCallBackHandler", "ctx", "null") Dim scr As String = "function CallTheServerCallBack(arg, ctx){ " _ & refscr & "; }" Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _ "CallTheServerCallBack", scr, True) End Sub </script> <html> <head runat="server"> <title>Untitled Page</title> </head> <body> <form runat="server"> <script language="javascript"> function CallTheServer() { var zc = document.forms[0].elements['zipcode']; CallTheServerCallBack(zc.value, zc.id); } function MyCallBackHandler(result, context) { l = document.forms[0].elements['resultlabel']; l.value = "Info: " + result + ' - extracted from ' + context; } </script> <input type="text" id="zipcode" name="zipcode"></input> <button id="myBtn" onclick="CallTheServer()">Validate</button> <br /> <textarea id="resultlabel" name="resultlabel" rows="10" cols="60"></textarea> </form> </body> </html> The Page_Load event is exactly as we've discussed in earlier sectionsit simply constructs the CallTheServerBack function, which wraps the _ _doCallback function. The server ClientCallBackFunction event simply returns a stringin a real application this perhaps would perform some sort of data lookup. On the client side there is a button with an onClick event procedure, which takes the value from the top text box ( zipcode ) and the id and calls the server callback routine with those values. Giving the CallTheServer CallBack function two parameters allows for a flexible approach, where the value and context can be passed in. You could also do this for the callback functions if required. Finally, the MyCallBackHandler function just accepts the result and context from the callback handler and displays them in the text area. You can see that although there are several steps in this code, it's actually quite simple. This example uses an ASP.NET page, but it's just as easy to put the implementation into a custom server control. Although this technique relies on XmlHTTP , it is cross-browser friendly and works on Netscape Navigator (see Figure 9.9). Figure 9.9. Client callbacks on Netscape Navigator Uses for Client CallbacksClient callbacks are perfect for those situations where you want to provide quick access to server functionalitythis approach avoids the rendering phase for a page or control. This could be used for validation, preloading of data, or even timed execution of server code to simulate a push scenario. Another perfect situation for the use of client callbacks is to solve a problem discussed earlier in the book, in Chapter 7's Allowing User Selection of Skins subsection. There we discussed the problem of a theme browser that would allow users to select a theme to see how it looks. The problem was that the server event procedure that resulted from the selection of the theme fires too late for the theme to be appliedremember that themes have to be applied in the PreInit event. With this new technique, selection of the theme can now perform a client callback that sets the theme, for example: Function MyCallBackEvent(EventArgument As String) As String _ Implements ICallbackEventHander.RaiseCallbackEvent Profile.Theme = EventArgument End Function The client callback function can then simply invoke a standard postback: <script language="javascript"> function MyCallBackHandler(result,context) { __doPostBack('btnUpdate',,) } </script> The real postback will then run the PreInit event, at which stage the theme can be read from the Profile . Now a single selection can change the theme of a page. ![]() |