Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 57 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
57
Dung lượng
869,61 KB
Nội dung
252 CHAPTER 7 Under the hood of the UpdatePanel Now, when a request is made, you check to see if you’re in the middle of an asyn- chronous postback. If so, then you cancel the latest request to give precedence (priority) to the previous postback. To accomplish this, make the updates to the onInitializeRequest handler shown in listing 7.10. function onInitializeRequest(sender, args){ var prm = Sys.WebForms.PageRequestManager.getInstance(); var details = "postBackElementID = " + args.get_postBackElement().id; if (prm.get_isInAsyncPostBack()){ if (args.get_postBackElement().id == "Abort") prm.abortPostBack(); else{ args.set_cancel(true); details += " (canceled)"; } } var row = createEventRow("initializeRequest", details); $get('clientEvents').appendChild(row); } We may be lazy, but we pride ourselves on being proactively lazy. This example makes a copy of an instance of the B PageRequestManager so you can use it later in the function without all the extra typing. Next, you check to see if you’re cur- rently in an asynchronous postback by getting the C isInAsyncPostBack prop- erty from the PageRequestManager. This makes sense because you want to abort and cancel a request only if you’re currently in the middle of one. Finally, if the Abort button wasn’t the element that invoked the request, you set the D cancel property to true to give priority to the previous request. 7.2.5 Notifying the user Just before an asynchronous request is sent to the server, the PageRequestManager raises the beginRequest event. Similar to the previous example, the BeginRequest- EventArgs passed into this handler includes the postBackElement property. When raised, this occurrence gives you the opportunity to notify the user about the upcoming postback before it begins. For lengthy operations, what typically happens (and is recommended) is that the user is given a visual prompt signifying that work is in progress. The prompt is removed when the process is completed. Listing 7.11 demonstrates how you add this behavior to the existing application. Listing 7.10 Aborting and canceling requests during partial updates Save instance of PageRequestManager B Check if in asychronous postback C Cancel latest request D A client-side event viewer 253 <div id="loadingPanel" class="asyncPostBackPanel" style="display: none;"> <img src="images/indicator.gif" alt="" /> Loading </div> function onBeginRequest(sender, args){ $get('loadingPanel').style.display = 'block'; var row = createEventRow("beginRequest", ""); $get('clientEvents').appendChild(row); } function onEndRequest(sender, args){ $get('loadingPanel').style.display = 'none'; var row = createEventRow("endRequest", ""); $get('clientEvents').appendChild(row); } During the postback, the visual prompt you’d like to display to the user is declared in a div element called B loadingPanel . When the onBeginRequest function is invoked, the element is displayed by C changing its style. To complete the process, when the onEndRequest function is called, you hide the element by setting the D style back to its original state. The next step is the server-side pro- cessing of the request. 7.2.6 Locked and loaded Where are you in the process? Let’s quickly recap. You’ve invoked the request and passed the stage where it could have been aborted or canceled gracefully. In addi- tion, you’re displaying to the user an indication that an update or request is being processed—there is no turning back now! In between the beginRequest and endRequest events raised by the PageRe- questManager are two additional events that notify you about the progress of the postback on the server. The first event, pageLoading , occurs when the most recent postback has been received but before any updates to the interface are applied. Passed in to the arguments is information about which UpdatePanel controls will be updated and deleted. The second event, pageLoaded , is raised after the contents on the page have been rendered. This event also tells you which panels were created and updated. Listing 7.12 shows how you add this information to the event viewer application. Listing 7.11 Show and hide a visual prompt to the user during asychronous operations. Visual prompt B Show prompt C Hide prompt D 254 CHAPTER 7 Under the hood of the UpdatePanel function onPageLoading(sender, args){ var details = new Sys.StringBuilder(); details.append(displayPanels("Updating", args.get_panelsUpdating())); details.append(" - "); details.append(displayPanels("Deleting", args.get_panelsDeleting())); var row = createEventRow("pageLoading", details.toString()); $get('clientEvents').appendChild(row); } function onPageLoaded(sender, args){ var details = new Sys.StringBuilder(); details.append(displayPanels("Created", args.get_panelsCreated())); details.append(" - "); details.append(displayPanels("Updated", args.get_panelsUpdated())); var row = createEventRow("pageLoaded", details.toString()); $get('clientEvents').appendChild(row); } function displayPanels(action, panels){ var sb = new Sys.StringBuilder(); sb.append(action + " " + panels.length + " panel"); if (panels.length >= 0) sb.append("s"); if (panels.length > 0){ sb.append(" = "); for (var i = 0; i < panels.length; i++){ if (i > 0) sb.append(", "); sb.append(panels[i].id); } } return sb.toString(); } In the onPageLoading function, you retrieve the panels that are B updating and C deleting from the PageLoadingEventArgs object. To display information about each of them, you call a local utility function called F displayPanels , which for- mats the details for the event viewer. Listing 7.12 Determine which panels are being rendered as a result of a partial postback. Panels updating B Panels deleting C Panels created D Panels updated E Format details F A client-side event viewer 255 You follow a similar pattern in the onPageLoaded function by accessing the panels that are D created and E updated from the PageLoadedEventArgs object. The displayPanels function is leveraged again to update the viewer. After these occurrences, the endRequest event is raised by the PageRequestManager, thus completing a successful partial-page update. But what if something doesn’t go smoothly? What happens when an error occurs on the server during the postback processing? This question leads us to the last feature in the event viewer project: error handling. 7.2.7 Client-side error handling Regardless of whether an error occurs during an asynchronous postback, the PageRequestManager always raises the endRequest event. Passed into the handler for this occasion is an instance of the EndRequestEventArgs object. If an error occurs, it can be retrieved with the error property. If you decide to handle the error, you can update the errorHandled member to prevent it from being thrown on the page, resulting in an unfriendly dialog box. To validate these statements, let’s add a button to the page that throws an error when it’s clicked; see listing 7.13. <asp:Button ID="ThrowError" runat="server" Text="Throw Error" OnClick="ThrowError_Click" /> protected void ThrowError_Click(object sender, EventArgs e) { throw new InvalidOperationException("Nice throw!"); } Now, let’s capture the error and handle it in the event handler so it doesn’t display that unfriendly dialog box we mentioned earlier. Listing 7.14 illustrates the updated handler for the endRequest event. function onEndRequest(sender, args){ var details = ""; var error = args.get_error(); if (error != null){ details = "Error: " + error.message; args.set_errorHandled(true); } else details = "No errors"; Listing 7.13 Throw an unfortunate, but polite, error. Listing 7.14 Handling an error from the client Error check B Handle error C 256 CHAPTER 7 Under the hood of the UpdatePanel $get('loadingPanel').style.display = 'none'; var row = createEventRow("endRequest", details); $get('clientEvents').appendChild(row); } The B error property is retrieved from the arguments in the handler. If an error occurs, you update the client-side event details accordingly and set the C errorHandled property to true . This completes the event viewer application! You implemented a simple (but sharp looking) application that displays the client-side events that occur during a partial-page update. In the process, you picked up valuable knowledge about each of the events and how to exert more control over the application. Let’s take this pow- erful knowledge a step further and begin to investigate more complex scenarios. 7.3 UpdatePanel cookbook The beginning of this section marks an important milestone in the chapter. At this point, you should have a firm grasp of how the partial-page rendering mecha- nism works. You should have also picked up the tools necessary to take more con- trol of the application during asynchronous postbacks. With this knowledge at your disposal, we can now tackle more intricate and challenging problems. When we put together the content for this portion of the chapter, we decided to do something a bit different. First, we monitored the ASP.NET forums (see http://forums.asp.net/default.aspx?GroupID=34) for difficult problems develop- ers were running into. We then put together a set of solutions to those problems that we could present here, after a strong foundation was established, to demon- strate both limitations and creative techniques. Sometimes, when technical books present this type of format, they call it a cookbook—hence the title for the section. What follows are the recipes for success. 7.3.1 Why is the UpdatePanel slow? Sometimes, when the UpdatePanel contains many controls, a significant drop in performance occurs. As partial postbacks are invoked, the controls in the UpdatePanel begin to take a long time to render. This is most commonly observed when a GridView is used, particularly when many rows are displayed on the control. UpdatePanel cookbook 257 Figure 7.6 shows the steps that occur after the server-processing portion of an asynchronous postback is complete. Just before the old markup is replaced with the updated HTML, all the DOM elements in the panel are examined for Microsoft Ajax behaviors or controls attached to them. To avoid memory leaks, the components associated with DOM elements are disposed, and then destroyed when the HTML is replaced. As the number of elements in the page region increases, this phase of the partial-page update can take a while. Solution The following solution works only if the elements in the UpdatePanel aren’t asso- ciated with any Microsoft Ajax components or behaviors, including extenders. By disassociating the GridView from its parent node, the PageRequestManager bypasses the time-consuming step of checking for any leaks in the elements. List- ing 7.15 demonstrates how this can be accomplished with a GridView control. Update UpdatePanel Dispose components Replace HTML Figure 7.6 After returning from an asynchronous postback, all the components associated with elements in the UpdatePanel are disposed. 258 CHAPTER 7 Under the hood of the UpdatePanel <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:GridView ID="GridView1" runat="server" /> </ContentTemplate> </asp:UpdatePanel> <script type="text/javascript"> <! var pageRequestManager = Sys.WebForms.PageRequestManager.getInstance(); pageRequestManager.add_pageLoading(onPageLoading); function onPageLoading(sender, e) { var gv = $get("GridView1"); gv.parentNode.removeNode(gv); } // > </script> If you subscribe to the B pageLoading event raised by the PageRequestManager, then you can get a reference to the GridView’s container element and C remove it. As a result, the PageRequestManager won’t iterate through the elements in the GridView, and the new HTML will replace the old. TIP If you have multiple controls in an UpdatePanel that are also not associ- ated with any behaviors or components, try placing them in a common container element, such as a div . Then, you can remove the parent node of the common container element instead of removing the container for each of the controls. We hope that enhancement will come in handy one day. Next, let’s talk about how to handle dynamic scripts. 7.3.2 Inject JavaScript during a partial postback If you’ve ever inserted dynamic JavaScript into a page, then you’re most likely familiar with the ClientScriptManager class. Accessible through the Client- Script property of the Page instance, this class exposes a number of useful meth- ods. Among these methods are techniques for inserting JavaScript code and code blocks into a page: Listing 7.15 Removing the parent node to speed up the rending process Handler for pageLoading event B Bypass check for leaks C UpdatePanel cookbook 259 ■ RegisterClientScriptBlock —Injects JavaScript code anywhere on the page, depending on when the method is called ■ RegisterStartupScript —Injects JavaScript at the end of the page, ensur- ing that the script is parsed and loaded after the page has been rendered by the browser ■ RegisterClientScriptInclude —Injects a script tag with an src attribute that specifies the location of a JavaScript file to load ■ RegisterClientScriptResource —Similar to the previous call, except the src attribute specifies the location of a JavaScript file embedded in an assembly as a web resource If you’ve called any of these methods during an asynchronous postback, you may have noticed that they no longer work reliably, and in most cases don’t work at all. When an asynchronous postback occurs, the PageRequestManager antici- pates that the data coming from the server is formatted in a special way. Earlier, in section 7.1.2, we mentioned this awkward format by examining what gets returned from the server as a response to an asynchronous request. Along with the new HTML for the page, the incoming payload includes the updated View- State of the page and other helpful information. Because these methods were around long before ASP.NET AJAX came into the picture, it makes sense that they no longer work in this context—they don’t comply with the new format. What is the solution? When you’re working with UpdatePanel controls and partial postbacks, you must use a set of APIs provided by the ScriptManager that supplements the previ- ously mentioned methods. Luckily, the methods are basically the same to the caller—taking in an additional parameter that defines what invoked the script (the Page or a Control ). These methods are aware of the format the PageRequest- Manager expects and configure the injected script accordingly so the incoming data from the server can be interpreted in the browser. Web controls in the UpdatePanel If you’ve wrapped a web control in an UpdatePanel and would like to inject script into the page, you must use the methods provided by the ScriptManager to make it compatible. In addition, if you’re using third-party controls that no longer work in an UpdatePanel, the reason is most likely that they call the traditional methods for injecting script into the page. For a resolution, download and install the latest patches or updates from the vendor to add support for ASP.NET AJAX. 260 CHAPTER 7 Under the hood of the UpdatePanel Listing 7.16 demonstrates how to use one of the new APIs provided by the Script- Manager to inject JavaScript at the end of the page. string msg = string.Format("alert(\"{0}\");", "You've done this before, haven't you?"); ScriptManager.RegisterStartupScript(TestButton, typeof(Button), "clickTest", msg, true); In this example, you format the message—a simple alert call—and then call the ScriptManager’s static RegisterStartupScript method to dynamically place script at the end of the page. The only difference in the method call is the first parameter, which you use to pass in the instance of the Button control that invoked the insert. Because we’re on the topic of things that you must change in existing and pre- vious code, let’s look at those useful validator controls we’ve been so faithful to over the years. 7.3.3 Getting the validators to work Just like the registered scripts in the previous section, you may also have noticed dur- ing your ASP.NET AJAX development that the ASP.NET 2.0 validator controls aren’t compatible with the UpdatePanel. As a temporary fix, the ASP.NET team has re- leased the source code for a set of compatible validator controls. NOTE At the time of this writing, the controls are available as a download that you must apply. Future plans are to deploy the new validator controls through the Windows Update mechanism. If you’ve already installed this update, you can skip this section. To replace the old controls with the new and improved ones, you must compile the source code and then reference the assemblies for your website (we also pro- vide the assemblies in the source code for this chapter, on the book’s website). You can do this by using the Add Reference dialog in Visual Studio. When you’re developing a website (in contrast to a web application project) you can also copy the binaries into the bin folder and then refresh the folder. After you add the references to the new controls, you have to make a few mod- ifications to the site’s web.config file to complete the transition. What’s left is to use Listing 7.16 Registering script with the ScriptManager ensures that it’s UpdatePanel-compatible. UpdatePanel cookbook 261 a technique called tag mapping to re-map the old controls to the new ones in an ele- gant fashion. This method allows you to preserve all the declarative code you’ve implemented with the existing validator controls. The other advantage of this approach is that when the new validator controls are eventually deployed from Win- dows Update, the only changes you’ll have to make are removing the compiled bina- ries ( DLL files) from the bin folder and the tag-mapping setting in web.config. Listing 7.17 shows how to apply the tag mapping to the web.config file. <tagMapping> <add tagType="System.Web.UI.WebControls.CompareValidator" mappedTagType="Sample.Web.UI.Compatibility.CompareValidator, Validators, Version=1.0.0.0"/> <add tagType="System.Web.UI.WebControls.CustomValidator" mappedTagType="Sample.Web.UI.Compatibility.CustomValidator, Validators, Version=1.0.0.0"/> <add tagType="System.Web.UI.WebControls.RangeValidator" mappedTagType="Sample.Web.UI.Compatibility.RangeValidator, Validators, Version=1.0.0.0"/> <add tagType="System.Web.UI.WebControls.RegularExpressionValidator" ➥ mappedTagType="Sample.Web.UI.Compatibility. ➥ RegularExpressionValidator, Validators, Version=1.0.0.0"/> <add tagType="System.Web.UI.WebControls.RequiredFieldValidator" ➥ mappedTagType="Sample.Web.UI.Compatibility. ➥ RequiredFieldValidator, Validators, Version=1.0.0.0"/> <add tagType="System.Web.UI.WebControls.ValidationSummary" mappedTagType="Sample.Web.UI.Compatibility.ValidationSummary, Validators, Version=1.0.0.0"/> </tagMapping> Keeping up the pace of resolving complex issues, the next challenge is one you may have come across recently in your ASP.NET AJAX development. If not, you’ll most likely be faced with it someday soon. 7.3.4 Sys.WebForms.PageRequestManagerParseErrorException While working with the UpdatePanel control, you’ll probably run into this long but descriptive exception. Although this message may sound more like a medical Listing 7.17 Exchange existing validators with the new set while preserving the tag. [...]... dedicated to Ajax- enabled controls 2 76 CHAPTER 8 ASP.NET AJAX client components The $create method works in conjunction with $find to help manage client components instantiated in a web page In the following section, we’ll provide more insight on the $find method 8.2.2 Accessing components Once a client component has been correctly instantiated and added to a container, you can access it by passing its... responsible for creating, configuring, and initializing a client component instance Working with client components 275 to passing empty objects, as in figure 8.5, you can pass null In the subsequent listings, we’ll pass the empty object {} to evidentiate the type of the parameter To explain the remaining arguments, let’s return to the $create statement used in listing 8.1 to create an instance of the trivial... instantiated in the page 8.1.4 Containers A container is an object that holds a collection of «interface» child components and provides services to those Sys.IContainer +addComponent() components Typically, a container exposes meth+removeComponent() ods for adding, removing, and accessing the child +findComponent () components The Microsoft Ajax Library defines +getComponents() the Sys.IContainer interface... CHAPTER 8 ASP.NET AJAX client components Creating a TrivialComponent instance with the new operator is just the first step The next (optional) thing to do is configure the instance by setting client properties For example, the id property lets you retrieve a reference to the new instance using the $find method Once the initial configuration is complete, you must do the following: 1 Call the initialize... client components instantiated in the page As an example of a client class that is you’ll discover in the following sections, hosting cli- a container ent components in a container has various advantages For example, you can retrieve references to client components through the container, instead of storing them in global JavaScript variables Another benefit of hosting components in the Application object... becomes the container of all the client components instantiated using $create If you pass the ID of a component to $find, you get back the corresponding instance We’ll talk more about IDs and the $find method in section 8.2.2 In the meantime, look at figure 8.5 to see the component in action Working with client components 273 Figure 8.5 The trivial component greets you Before we discuss in detail how... participate in the lifecycle of a component, you need to override the initialize and dispose methods in the constructor’s prototype object Method overriding was explained in chapter 3, when we talked about inheritance in the Microsoft Ajax Library In the example, you override both methods to display a message box using the JavaScript alert function NOTE Don’t forget to call the base implementations of the initialize... and dispose methods using the callBaseMethod method, as in listing 8.1 They perform important processing steps during the initialization and disposing phases of the component’s lifecycle Calling the base implementations ensures that a component is properly initialized and disposed The trivial component also defines a method called greet This method displays a greeting message using the alert function... class, ASP.NET AJAX client components derive from the client Sys.Component class In the MicrosoftAjax.js file, the Sys.Component class is registered as follows: Sys.Component.registerClass('Sys.Component', null, Sys.IDisposable, Sys.INotifyPropertyChange, Sys.INotifyDisposing); As we said, one of the main characteristics of components is that they implement a definite set of interfaces Knowing which interfaces... passing its ID to the $find method Recall that every component exposes a property named id, which is defined in the base Sys.Component class, as shown in figure 8.1 The ID of a component can be passed to $find to retrieve a reference to the component itself, as shown in figure 8.7 Component ID var instance = $find('myComponentID', someContainer); IContainer instance Figure 8.7 $find lets you access a . Typically, a container exposes meth- ods for adding, removing, and accessing the child components. The Microsoft Ajax Library defines the Sys.IContainer interface for implementing containers. The. the ASP. NET AJAX frame- work, to keep you on your toes. The next chapter takes you on a journey into how client-side components are authored with the Microsoft Ajax Library. 264 ASP. NET AJAX client. the initialize and dispose methods in the constructor’s prototype object. Method overriding was explained in chapter 3, when we talked about inheritance in the Microsoft Ajax Library. In the