2. Handling Errors
As you've seen,
when the UpdatePanel performs its callback, the web page code runs in
exactly the same way as if the page had been posted back. The only
difference is the means of communication (the page uses an asynchronous
call to get the new data) and the way the received data is dealt with
(the UpdatePanel refreshes its inner content, but the remainder of the
page is not changed). For that reason, you don't need to make
significant changes to your server-side code or deal with new error
conditions.
That said, problems can
occur when performing an asynchronous postback just as they do when
performing a synchronous postback. To find out what happens, you can add
code like this to the event handler for the Page.Load event, which
causes an unhandled exception to occur when an asynchronous callback
takes place:
If Me.IsPostBack Then
Throw New ApplicationException("This operation failed.")
End If
When the web page throws an
unhandled exception, the error is caught by the ScriptManager and passed
back to the client. The client-side JavaScript then throws a JavaScript
error. What happens next depends on your browser settings, but usually
browsers are configured to quietly suppress JavaScript errors. In
Internet Explorer, an "Error on page" message appears in the status bar
that indicates the problem. If you double-click this notification, a
dialog box appears with the full details, as shown in Figure 3.
There's another option for
dealing with the errors that occur during an asynchronous postback. You
can use custom error pages, just as you do with ordinary web pages. All
you need to do is add the <customErrors> element to the
web.config file.
For example, here's a <customErrors> element that redirects all errors to the page named ErrorPage.aspx:
<configuration>
<system.web>
<customErrors defaultRedirect="ErrorPage.aspx" mode="On"></customErrors>
...
</system.web>
</configuration>
Now, when the
PageRequestManager is informed of an error it will redirect the browser
to ErrorPage.aspx. It also adds an aspxerrorpath query string argument
to the URL that indicates the URL of the page where the problem
originated, as shown here:
http://localhost/Ajax/ErrorPage.aspx?aspxerrorpath=/Ajax/UpdatePanels.aspx
You can write code in
ErrorPage.aspx that reads the aspxerrorpath information. For example,
you might include a button that redirects the user to the original
requested page, like this:
Dim url As String = Request.QueryString("aspxerrorpath")
If url <> "" Then Response.Redirect(url)
If your website uses custom
error pages but you don't want them to apply to asynchronous postbacks,
you must set the ScriptManager.AllowCustomErrorsRedirect property to
false.
NOTE
Several controls
don't work correctly in an UpdatePanel or don't work in specific
scenarios. Most notably, the FileUpload and HtmlInputFile controls don't
work at all. The Login, PasswordRecovery, ChangePassword, and
CreateUserWizard controls work only if you've converted their content to
templates. The GridView and DetailsView controls fail if you've set
EnableSortingAndPagingCallbacks to True. And the TreeView and Menu
controls don't work if you've set their style properties (instead, you
can format their items using CSS styles). However, all these controls
will work on pages that contain UpdatePanel controls, so long as they
aren't actually in an UpdatePanel.
3. Conditional Updates
In complex pages, you might
have more than one UpdatePanel. In this case, when one UpdatePanel
triggers an update, all the UpdatePanel regions will be refreshed.
If you have more than one
UpdatePanel and each one is completely self-contained, this isn't
necessary. In this situation, you can configure the panels to update
themselves independently. Simply change the UpdatePanel.UpdateMode
property from Always to Conditional. Now, the UpdatePanel will refresh
itself only if an event occurs in one of the controls in that
UpdatePanel.
To try this out, create a page
that has several UpdatePanel controls, each with its own time display
and button. Then, add code that places the current time in the label of
all three controls:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) _
Handles Me.Load
lblTime1.Text = DateTime.Now.ToLongTimeString()
lblTime2.Text = DateTime.Now.ToLongTimeString()
lblTime3.Text = DateTime.Now.ToLongTimeString()
End Sub
Now, when you click one of the
Refresh Time buttons, only the label in that panel will be updated. The
other panels will remain untouched.
NOTE
There's an interesting
quirk here. Technically, when you click the button all the labels are
updated, but only part of the page is refreshed to show that fact. The
next time the page is posted back, the most recent values are pulled out
of view state and applied to all the labels, including the ones that
weren't refreshed on the client.
Most of the time, this
distinction isn't important. But if this isn't the behavior you want in
this example, you could use a separate event handler for each button.
Each event handler would update just one label—the label that's in the
appropriate UpdatePanel. That way, when the page is posted back just one
label is changed, and you don't waste time changing parts of the page
that won't be updated in the browser.
There's one caveat with
this approach. If you perform an update that takes a long time, it could
be interrupted by another update. As you know, ASP.NET AJAX posts the
page back asynchronously, so the user is free to click other buttons
while the postback is under way. ASP.NET AJAX doesn't allow concurrent
updates, because it needs to ensure that other information—such as the
page view state information, the session cookie, and so on—remains
consistent. Instead, when a new asynchronous postback is started, the
previous asynchronous postback is abandoned. For the most part, this is
the behavior you want. If you want to prevent the user from interrupting
an asynchronous postback, you can add JavaScript code that disables
controls while the asynchronous postback is under way, but this takes
significantly more work.
There's one other way to
update a conditional UpdatePanel—you can call the UpdatePanel.Update()
method from your server-side code. This allows you to decide on the fly
whether a certain panel should be refreshed. However, you must be
careful not to call Update() on a panel that uses an UpdateMode of
Always, and you must not call Update() after the page has been rendered.
If you make either of these mistakes, you'll cause an exception. |