Thank you Keith Brooks for contributing to this article.
If you have ever tried to capture exceptions in your codebehind and display
friendly messges the users using Microsoft’s AJAX toolkit, you may find the very
simple idea becomes complicated very quickly. I ran into this when I had some
projects that I wanted to AJAX’ify by using a typical alert method that
registered a block of client side javascript and was fired on the pages Load
event, but found that method failed when using AJAX.
Let me explain why… when a normal ASP.NET page renders it goes through a
typical a Page Life Cycle . For normal page lifecycle, the script blocks
are written to the client during the Initialization event and once complete the
Load event code executes.
However, with a ASP.NET AJAX that page life cycle is somewhat different. On
the initial page load, the page loads using the standard ASP.NET Page Lifecycle;
however, when posting data back to the server using an AJAX update panel not all
of the page is being sent back to the server, hence the AJAX. Because of this
when the AJAX post to the server is complete, the entire page is not
re-rendered. Only the contents of the update panel is updated and no page level
Load event fires.
Here lies the issue. Since the page does not completely re-render and there
is no client side Page Load event after the postback, many of the old scripts
for displaying error messages do not work.
Ok. Enough of the lecture. You probably want to learn the
solution.
There are a few parts to this solution. First, the ScriptManager object has a
relatively obscure property called “onasyncpostbackerror”. This property
represents the code behind function (server side) that gets fired when an
exception happens during a post back. So the first step is to register a
function using this property.
<asp id="ScriptManager1" runat="server" onasyncpostbackerror="ScriptManager1_AsyncPostBackError"></asp>
Next step is to write the code behind’s AsyncPostBack error method. In my
case, I had already handled the exception, wrapped it, and threw the user
friendly exception, therefore this was all I needed in my code behind. Realize
that you must raise the exception and leave it unhandled for it to bubble up to
the Script Manager’s event to handle it.
protected void ScriptManager1_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
ScriptManager1.AsyncPostBackErrorMessage = e.Exception.Message;
}
Here is a simple demonstration of how I set up forced the exception.
protected void Button1_Click(object sender, EventArgs e)
{
try
{
DoingSomething();
}
catch (Exception ex)
{
throw new Exception("The system has encountered problems, please notify your administrator or try again.", ex);
}
}
private void DoingSomething()
{
throw new Exception("inner exception message");
}
Now back to the client side of the example. I have to register an event in
javascript to handle the ‘EndRequestHandler’ event created by the AJAX post
back. This event fires upon the return on the post back to the client and I also
added some features to remove the default exception,
“Sys.WebForms.PageRequestManagerServerErrorException:”, and I’m displaying the
Exception using a Panel control and the ModalPopupExtender (shown below) using
the MS AJAX ModalPopupExtender
object.
Sys.WebForms.PageRequestManager.getInstance().
add_endRequest(EndRequestHandler);
function EndRequestHandler(sender, args)
{
if (args.get_error() != undefined && args.get_error().httpStatusCode == '500')
{
var errorMessage = args.get_error().message
errorMessage = errorMessage.replace('Sys.WebForms.
PageRequestManagerServerErrorException:', "");
args.set_errorHandled(true);
$get("txtAlertMessage").innerHTML = errorMessage;
$find("ModalAlertDialogBehavior").show();
}
}
Here is the source of the Panel I use and the ModalPopupExtender:
{Note: for the the modal dialog extender you need to bind the control to a
button, but that button may be hidden, as you will see in my source code. I call
the Show() method of the Modal Dialog’s Behavior. You do not call show on the
modal dialog object itself.}
<asp:Panel ID="AlertPanel" runat="server" CssClass="modalPopup">
<asp:Panel ID="AlertPanelContent" runat="server" >
<table class="AlertTable">
<tr class="AlertRow" height="20px">
<td class="AlertTitle" height="20px">
<center>
<asp:Label ID="lblAlertTitle" runat="server" Text="Label" height="20px"/>
</center>
</td>
</tr>
<tr class="AlertRow">
<td>
<div id="txtAlertMessage" class="AlertTextMessage" />
</td>
</tr>
<tr class="AlertRow">
<td>
<center>
<asp:Button ID="btnClose" runat="server" Text="Close" />
</center>
</td>
</tr>
</table>
</asp:Panel>
</asp:Panel>
<ajaxToolkit:ModalPopupExtender ID="ModalAlertDialog" runat="server"
BackgroundCssClass="modalBackground" TargetControlID="NullButton" PopupControlID="AlertPanel"
CancelControlID="btnClose" BehaviorID="ModalAlertDialogBehavior" DropShadow="false" />
<asp:Button ID="NullButton" runat="server" Text="" CssClass="AlertNullButton" />
{Note: you will need the css file for the ModalPopupExtender to display
correctly. }
Putting all the pieces together:
The button click event calls the ‘DoSomething’ method. The DoSomething method
throw an exception . The try catch block in the button click event catches the
exception and wraps it in an outer Exception which contains a user friendly
message. The ‘AsyncPostBackError’ method passes back the exceptions message and
the client side EndRequestHandler checks for the exception and displays the
message.
Here is my entire ASP.NET page code:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AlertExample._Default" %>
<%@ Register
Assembly="AjaxControlToolkit"
Namespace="AjaxControlToolkit"
TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<link href="Includes/Stylesheet1.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"
onasyncpostbackerror="ScriptManager1_AsyncPostBackError" >
</asp:ScriptManager>
<div>
<script type="text/javascript" language="javascript">
Sys.WebForms.PageRequestManager.getInstance().
add_endRequest(EndRequestHandler);
function EndRequestHandler(sender, args)
{
if (args.get_error() != undefined && args.get_error().httpStatusCode == '500')
{
var errorMessage = args.get_error().message
errorMessage = errorMessage.replace('Sys.WebForms.
PageRequestManagerServerErrorException:', "");
args.set_errorHandled(true);
$get("txtAlertMessage").innerHTML = errorMessage;
$find("ModalAlertDialogBehavior").show();
}
}
</script>
</div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" >
<ContentTemplate>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Panel ID="AlertPanel" runat="server" CssClass="modalPopup">
<asp:Panel ID="AlertPanelContent" runat="server" >
<table class="AlertTable">
<tr class="AlertRow" height="20px">
<td class="AlertTitle" height="20px">
<center><asp:Label ID="lblAlertTitle" runat="server" Text="Label" height="20px"/>
</center>
</td>
</tr>
<tr class="AlertRow">
<td>
<div id="txtAlertMessage" class="AlertTextMessage" />
</td>
</tr>
<tr class="AlertRow">
<td>
<center>
<asp:Button ID="btnClose" runat="server" Text="Close" />
</center>
</td>
</tr>
</table>
</asp:Panel>
</asp:Panel>
<ajaxToolkit:ModalPopupExtender ID="ModalAlertDialog" runat="server"
BackgroundCssClass="modalBackground" TargetControlID="NullButton" PopupControlID="AlertPanel"
CancelControlID="btnClose" BehaviorID="ModalAlertDialogBehavior" DropShadow="false" />
<asp:Button ID="NullButton" runat="server" Text="" CssClass="AlertNullButton" />
</form>
</body>
</html>
And here is my entire codebehind class:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
namespace AlertExample
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
lblAlertTitle.Text = "Alert Example";
}
protected void Button1_Click(object sender, EventArgs e)
{
try
{
DoingSomething();
}
catch (Exception ex)
{
throw new Exception("The system has encountered problems, please notify your administrator or try again.", ex);
}
}
private void DoingSomething()
{
throw new Exception("inner exception message");
}
protected void ScriptManager1_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
ScriptManager1.AsyncPostBackErrorMessage = e.Exception.Message;
}
}
}
And finally the entire CSS:
/*Popup Control*/
/*Modal Popup*/
.modalBackground {
background-color:Gray;
filter:alpha(opacity=70);
opacity:0.7;
}
.modalPopup {
background-color:#FFF;
border-width:1px;
border-style:solid;
border-color:ActiveCaption;
padding:0px;
width:400px;
height:260px;
vertical-align:middle;
}
.AlertOkButton
{
text-align:center;
}
.AlertTable
{
width:100%;
height:100%;
border-spacing:0px;
margin-left:0px;
margin-right:0px;
padding:0px;
}
.AlertRow
{
padding:2px;
}
.AlertTitle
{
background-image:url('../includes/Office2003BlueBG.png');
background-repeat:repeat-x;
background-color:ActiveCaption;
color:White;
font-weight:bold;
width:100%;
height:21px;
max-height:21px;
}
.AlertRow
{
padding:2px;
}
.AlertTextMessage
{
width:350px;
min-height:180px;
max-height:180px;
color:Black;
background-color:#FFF;
padding:0px;
border-spacing:0px;
margin-left:20px;
margin-top:20px;
}
.AlertNullButton
{
visibility:hidden;
}
Sample
output of the AJAX Modial Dialog Example
There are a few more tweaks needed to improve how it displays, but I’ll leave
that to you. Happy coding!!!