Few days ago, when I was trying to put validation controls on a form which is at the bottom of the page. I noticed that each time Asp.net validation controls executed it reset the scroll position to top and the end user see no messages until page scroll back to the old position. Irritated :)
Problem:
So, In this post I will demonstrate you how to maintain the scroll position of asp.net validation controls on a page. To start with an example, lets create a form like below
<div>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<p>
</p>
<table class="style1">
<tr>
<td>
Name</td>
<td>
<asp:TextBox ID="txtName" runat="server" autocomplete="off" Width="200"></asp:TextBox>
<asp:RegularExpressionValidator ID="revName" runat="server"
ControlToValidate="txtName" Display="None"
ErrorMessage="Please enter a valid name"
ValidationExpression="(^([0-9-a-z-A-Z\\s+?\\.+?\\(+?\\)+?])+$)"
ValidationGroup="grpTop"></asp:RegularExpressionValidator>
<asp:RequiredFieldValidator ID="rfvName" runat="server"
ControlToValidate="txtName" Display="None" ErrorMessage="Please enter a name."
SetFocusOnError="true" ValidationGroup="grpTop"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>
Email Address</td>
<td>
<asp:TextBox ID="txtUsername" runat="server" autocomplete="off" Text=""
Width="200"></asp:TextBox>
<asp:RequiredFieldValidator ID="rfvEmailAddress" runat="server"
ControlToValidate="txtUsername" Display="None"
ErrorMessage="Please enter a valid email address." ValidationGroup="grpTop"></asp:RequiredFieldValidator>
<asp:RegularExpressionValidator ID="revEmail" runat="server"
ControlToValidate="txtUsername" Display="None"
ErrorMessage="Please key in a valid email address." SetFocusOnError="true"
ValidationExpression="^([a-zA-Z0-9_'+*$%\\^&!\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9:]{2,4})+$"
ValidationGroup="grpTop"></asp:RegularExpressionValidator>
<asp:CustomValidator ID="valEmailAlreadyExists" runat="server" Display="None"
ErrorMessage="Email address already exists" ValidationGroup="grpTop"></asp:CustomValidator>
</td>
</tr>
<tr>
<td>
Date Format</td>
<td>
<asp:DropDownList ID="ddlDateFormat" runat="server" Width="200px">
<asp:ListItem>Select</asp:ListItem>
<asp:ListItem Text="dd/MM/yyyy" Value="dd/MM/yyyy">dd/MM/yyyy</asp:ListItem>
<asp:ListItem Text="MM/dd/yyyy" Value="MM/dd/yyyy">MM/dd/yyyy</asp:ListItem>
<asp:ListItem Text="dd/MM/yy" Value="dd/MM/yy">dd/MM/yy</asp:ListItem>
<asp:ListItem Text="MM/dd/yy" Value="MM/dd/yy">MM/dd/yy</asp:ListItem>
</asp:DropDownList>
<asp:RequiredFieldValidator ID="rfvDateFormat" runat="server"
ControlToValidate="ddlDateFormat" Display="None"
ErrorMessage="Please select date format." InitialValue="Select"
SetFocusOnError="true" ValidationGroup="grpTop"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:CheckBox ID="cbIsAdmin" runat="server" Checked="false"
Text="Administrator?" />
<asp:CheckBox ID="cbResetPW" runat="server" Checked="false"
Text="Reset Password" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Button ID="Button1" runat="server" Text="Submit Group 1" ValidationGroup="grpTop"
onclick="Button1_Click" />
<asp:ValidationSummary ID="ValidationSummary1" runat="server"
EnableClientScript="true" HeaderText="Fix the following issues :"
ValidationGroup="grpTop" />
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
</table>
</div>
Notice that <p> tag which I have given in the above snippet to keep my form at the bottom of the page so that the scroll position problem can easily be demonstrated.
When you run the above snippet, you will realize that the page scroll position will lost as soon as you want to submit the form. If you try the page directive System.Web.Ui.Page.MaintainScrollPositionOnPostBack. Noting will change because by using this property Asp.net engine will take care of scroll position after post back. But we want it on client script. It means, we need to modify the mechanism on which asp.net validation controls executed.
To start with the understanding, I came across a very good article “Understanding ASP.NET Validation Library” by DeepakRaghavan. Which gives me the understanding of two methods. Page_ClientValidate and WebForm_OnSubmit and ultimately I need to modify them and bear in mind that these function will be written inside form tag not in title.
The idea is to use the bookmark to maintain the scroll bar. So lets modify both the functions respectively.
Page_ClientValidate:
1: var bookMark = "#OtherBookMark";
2: function Page_ClientValidate(validationGroup)
3: {
4:
5: Page_InvalidControlToBeFocused = null;
6: if (typeof(Page_Validators) == "undefined") {
7: return true;
8: }
9: var i;
10: for (i = 0; i < Page_Validators.length; i++) {
11: ValidatorValidate(Page_Validators[i], validationGroup, null);
12: }
13: ValidatorUpdateIsValid();
14: ValidationSummaryOnSubmit(validationGroup);
15: Page_BlockSubmit = !Page_IsValid;
16:
17: if (validationGroup == "grpTop")
18: {
19: bookMark = "#addForm";
20: }
21: else
22: {
23: bookMark = "#OtherBookMark";
24: }
25: return Page_IsValid;
26: }
Description:
Line No 1. A variable that will hold the bookmark name
Line No 2-14 The default function implementation (We have simple nothing to do with it… simply copy / paste)
Line No 15- 22 Since we can have multiple forms on a page, that is why it is better to keep track that which validation group is called and decide the bookmark name accordingly.
Now instead of modifying WebForm_OnSubmit, let’s first create a following function
SubmitAction:
1: function SubmitAction() {
2: if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false)
3: {
4: var hashIndex = location.href.indexOf("#");
5:
6: if (hashIndex > -1)
7: {
8: var loc = location.href.substring(0,hashIndex);
9: location.href = loc + bookMark;
10: }
11: else
12: {
13: location.href = location + bookMark;
14: }
15:
16: return false;
17: }
18: else
19: {
20: return true;
21: }
22: }
Description:
Well, the function is very simple and don’t really need line by line commentary.
We are checking if the validation is fail, set the bookmark. other wise just let it go.
WebForm_OnSubmit:
Now we will call the above SubmitAction function inside WebForm_OnSubmit. To inject this function you need to write the following lines on the Page_Load
1: if (!Page.ClientScript.IsOnSubmitStatementRegistered("keySubmitAction"))
2: {
3: Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "keySubmitAction", "return SubmitAction();");
4: }
System.Web.UI.Page.ClientScript.RegisterOnSubmitStatement is actually used to inject your script inside WebForm_OnSubmit. To be more specific, the first lines will be yours :).
and if you notice the Line No : 3 I used return statement which means as soon as WebForm_OnSubmit will execute my function (SubmitAction) will run first and then return the execution out… no more processing on WebForm_OnSubmit.
Now when you look at the view source of your browser you will see some thing similar to the following.
Ok, now we only need to setup our bookmark.
Just give the id “addForm” to the table or div inside which the validation summary control is placed. that’s it. However, you can download the VS 2008 Solution from here.