Using ASP.NET to Prompt a User to Save When Leaving a Page
By Scott Mitchell
Introduction
Last week I wrote an article titled Prompting a User to Save When Leaving a Page, which looked at how to use the client-side
onbeforeunload
event to display a confirmation messagebox when a
user attempted to leave a data-entry page after having modified the data's contents without explicitly saving the data.
To summarize last week's article, adding such a feature required the following steps:
- Writing code that saves the initial values of the page's input form fields in a client-side array.
- Creating an event handler for the
onbeforeunload
event that checks to see if the input form fields' values differ from those in the array. If they do, then a string is returned, prompting the user if they really want to leave. Otherwise, no string is returned, and the user can leave the page as they normally would (i.e., without being prompted). - Finally, for buttons or other HTML elements that could cause the user to leave the page but should not require
that the user be prompted, a client-side
onclick
event handler was used to set a flag that indicated that theonbeforeunload
event handler didn't need to check for changes. This is typically used with a "Save" button that, when clicked, causes a postback, but whose posting back should not prompt the user that they are about to leave the page.
<script>
blocks
and client-side onclick
events where needed. While adding this script code is not terribly difficult, I find
it simpler to move this logic to server-side methods that will inject the appropriate client-side script for us. In this
article we'll examine how to extend the base Page
class, adding a couple of methods that will allow for a user
to be prompted when they leave the page without saving without the page developer having to write a single line of client-side
script code. (If you've yet to read Prompting a User to Save When Leaving a Page,
please be sure to do so before continuing on.)
An Update to this Article... |
---|
Since the publication of this article I have received many questions from readers on a couple of issues - namely, being able
to suppress the prompt when using a Web control whose AutoPostback property is set to True, and how to suppress
client-side script error messages in Internet Explorer when leaving the page via client-side script using the eval()
function. These two questions are discussed and answered in An Update on Prompting a User to Save When Leaving an ASP.NET Page.
|
A High-Level Look at What We Want to Accomplish
The code-behind class for any ASP.NET Web page is derived, either directly or indirectly, from the
Page
class
in the System.Web.UI
namespace. The Page
class contains the base functionality that all ASP.NET
Web pages must provide. For example, the Page
class includes properties like IsValid
and IsPostBack
,
and events like Load
(which triggers the Page_Load
event handler to execute). If there is some functionality
that you know you will need in a number of pages, you can provide this functionality by creating a custom class that derives
from the Page
class, and then have your ASP.NET Web pages' code-behind classes derive from this custom class
(rather than from the Page
class directly).
In this article we'll create such a custom class that extends the Page
class. Our extended Page
class
will contain methods that we can call that will inject the appropriate client-side script so that if a user attempts to leave
the page after making changes without saving, a confirmation will be displayed. Specifically, there will be two methods:
MonitorChanges(webControl)
- this method accepts a Web control (such as a TextBox, CheckBox, DropDownList, etc.) and adds the necessary client-side script to monitor this control's value. That is, if this control's value is changed and then the user attempts to leave the page without saving, they'll be prompted with a warning.BypassModifiedMethod(webControl)
- this method is used to indicate that a particular Web control should not cause the confirmation to be displayed. This will typically be used on "Save" Buttons, LinkButtons - namely on Web controls that might cause a postback but, even if changes have been made, should not display the confirmation.
Page
class:
RegisterClientScriptBlock()
, RegisterStartupScript()
, and RegisterArrayDeclaration()
.
These three server-side methods add client-side code to the ASP.NET page's rendered markup. Let's take a brief moment to
examine these three methods.
Working with Client-Side Script Code in Server-Side Code
Working on Web applications requires a keen understanding of the logical, physical, and temporal differences. These differences were more apparent in classic ASP, but ASP.NET and its Web Forms paradigm effectively blurs the distinction between the client and server. Regardless, the distinction still very much exists and it is important to be familiar with the separation.
There are often times when we might want to inject client-side script from a server-side method. To facilitate this the
Page
class provides a number of methods. To add a block of client-side script code, use either
RegisterClientScriptBlock(key, script)
or RegisterStartupScript(key, script)
. These methods both accept two string
values as input: a key and a script value. The key uniquely identifies the script block being injected,
while the script value contains the actual client-side script to inject. (Note that the script input parameter
must include the precise markup to inject, including the <script>
tag itself.)
The main difference between these two methods is the location in the markup where the controls emits its script. Both methods
inject their script content inside the <form>
, but RegisterClientScriptBlock(key, script)
adds it before the Web controls within the form, whereas RegisterStartupScript(key, script)
adds
the script after the Web control markup.
The other Page
class that we'll need to utilize is the RegisterArrayDeclaration(arrayName, arrayValue)
.
This method creates a client-side array with the values specified. To create an array named foo
with values 1 through n
you'd call the RegisterArrayDeclaration(arrayName, arrayValue)
method n times, like so:
Page.RegisterArrayDeclaration("foo", "1");
|
A thorough discussion of the client-side injection methods in the Page
class is a bit beyond the scope of this
article. For more information, utilize the following two articles of mine: Working
with Client-Side Script and Injecting
Client-Side Script from an ASP.NET Server Control. (The first article also discusses in more detail the process of
extending the functionality of the Page
by creating a custom, derived class, and having ASP.NET pages' code-behind classes
deriving from this custom class.)
Creating the MonitorChanges(webControl)
Method
A page developer using our extended
Page
class - which I named ClientSidePage
- can indicate that a
particular Web control on the page should be monitored for changes by calling the MonitorChanges()
method, passing
in the Web control to watch. This method needs to do the following two tasks:
- Add the Web control's client-side
ID
to a client-side array. (This array is what is used to grab the initial values of the input controls, and, upon page exit, is used to check to see if the input fields' values have changed.) - Inject the client-side script that saves the initial form field values and that handles the
onbeforeunload
client-side event.
MonitorChanges()
method; the second task is delegated to an additional
method, as shown below (the complete ClientSidePage
class is available for download at the end of this article in both VB.NET
and C#):
Public Class ClientSidePage
|
Before delving into the methods, first note that the ClientSidePage
class derives from System.Web.UI.Page
.
It is vital that ClientSidePage
extend the Page
class in order to use this class as the base class for our ASP.NET
pages' code-behind classes.
For More Information on Base Classes... |
---|
The ClientSidePage class is an example of a base class that is designed to be inherited by an ASP.NET page's
code-behind class. This is a common practice in more involved ASP.NET Web applications and is discussed in more detail
in the article Using a Custom Base Class for your ASP.NET Page's Code-Behind Classes.
|
The MonitorChanges()
method's first task is to create an array that contains the client-side ID
s
of the input form fields to watch. This is done by creating two arrays: monitorChangesIDs
, which holds the
ID
s of the elements to watch; and monitorChangesValues
, which holds the initial values of
these form fields. The MonitorChanges()
method takes the passed-in Web control's ClientID
and adds
it to the first array, and then adds a null
to the second. (The second array is populated through client-side
code, which we'll examine in a bit.)
One thing to note is that the MonitorChanges()
checks to see if the Web control passed in is a RadioButtonList or
CheckBoxList. If it is one of these two types of Web controls, special care must be taken since these controls create a set of children radio buttons or checkboxes whose
client-side ID
s are of the form CheckBoxOrRadioButtonListID_indexOfCheckBoxOrRadioButton
.
That is, a RadioButtonList with an ID
of favSport
would have its rendered radio buttons with
ID
s of favSport_0
, favSport_1
, favSport_2
, and favSport_3
.
Therefore, for RadioButtonLists and CheckBoxLists, the MonitorChanges()
method registers each of the control's
corresponding radio buttons or checkboxes in the client-side array. (One thing to note is that for data-bound RadioButtonLists
and CheckBoxLists, you will need to call MonitorChanges()
after you do the databinding.)
After adding the client-side ID
s to the array, MonitorChanges()
calls the AssignMonitorChangeValuesOnPageLoad()
method, which does two things: injects startup script that calls the client-side assignInitialValuesForMonitorChanges()
function (this function then populates the monitorChangesValues
array with the specified form fields' initial values);
and injects the actual code for the assignInitialValuesForMonitorChanges()
function, along with the closeConfirm()
function (which serves as the event handler for the onbeforeunload
event).
The code in the client-side closeConfirm()
function is virtually identical to the code examined in the last week's
article, Prompting a User to Save When Leaving a Page.
The only difference is that here when a discrepancy is found and the user is prompted if they really want to exit the page,
we set the needToConfirm
flag to false
, but then setup a timer so that it's reset back to true
in 0.75 seconds. You may, understandably, be wondering why this is done.
The reason is, admittedly, a bit of a hack. To understand why we need this hack, realize that whenever a hyperlink is clicked,
the browser says, "Ok, you are leaving the page," and fires its onbeforeunload
event. This sounds fine, but things
can get a bit weird if the hyperlink contains client-side JavaScript in its href
that causes the page to unload
again. Specifically, if you have a hyperlink with an href
with code that submits the form or redirects the user, when that hyperlink is
clicked, the user will be prompted once, asking if they want to leave the page. If they click OK, the hyperlink's JavaScript
will run, submitting the form or redirecting the user, which causes the browser to again prompt the user, asking them if they are sure
they want to leave the page. To suppress this second message, we simply set needToConfirm
to false
for
0.75 seconds. What this does, is when the hyperlink is first clicked, it prompts the user. If the user clicks OK, and the JavaScript
kicks in, the JavaScript doesn't cause a second, superfluous confirmation messagebox (assuming the JavaScript can run and complete
in 0.75 seconds). This hack is especially pertinent to ASP.NET since LinkButtons are hyperlinks with JavaScript code in their
href
that submits (posts back) the form.
Excluding Buttons From Prompting the User
As we examined in Prompting a User to Save When Leaving a Page, the
onbeforeunload
event causes the user
to be prompted if they want to save when they exit the page after having made changes, but without saving. Specifically, this
onbreforeunload
event fires when the user closes their browser, enters another URL, clicks on a hyperlink, or
posts back the form. What we want to avoid is having the "Save" button display a confirmation messagebox. Rather, we want
the "Save" button to be able to cause a postback without warning the user that values have been changed.
To accomplish this we need to have the "Save" button's client-side onclick
event set the needToConfirm
flag to false
. This can be accomplished via the BypassModifiedMethod(webControl)
method, which
simply adds this needed client-side attribute to the passed-in Web control. The code for this method is rather simple, and shown below:
|
Using the ClientSidePage
Class in an ASP.NET Web Page
Let's wrap things up with looking at a simple data-entry Web page that utilizes the features we've examined thus far. Start by creating a Web page with a number of form fields, such as TextBoxes, CheckBoxes, DropDownLists, RadioButtonLists, and so on. Next, add a "Save" button. Now, to have the page exhibit the desired behavior - that is, to have it prompt the user if they make a change to one of the data entry form fields and attempt to leave the page without saving - do the following:
- In the page's code-behind class, have it derive from
ClientSidePage
- In the
Page_Load
event handler have a call to theMonitorChanges()
method for each data-entry Web control on the page. Do this on every page load (including postbacks). Also callBypassModifiedMethod()
, passing in any Buttons that should not cause the user to be prompted.
In the download at the end of this article, there's a sample ASP.NET page, WebForm1.aspx
, with a number of
data-entry form fields. Here's the server-side code for monitoring changes to these fields:
|
Notice that the code-behind class is derived from ClientSidePage
(and not System.Web.UI.Page
), that
there's a MonitorChanges()
call for each data-entry form field, and that there's a call to
BypassModifiedMethod()
for the "Save" button.
An Update to this Article... |
---|
Since the publication of this article I have received many questions from readers on a couple of issues - namely, being able
to suppress the prompt when using a Web control whose AutoPostback property is set to True, and how to suppress
client-side script error messages in Internet Explorer when leaving the page via client-side script using the eval()
function. These two questions are discussed and answered in An Update on Prompting a User to Save When Leaving an ASP.NET Page.
|
Happy Programming!
Attachments
ClientSidePage
class in C# code