Article Discussion: Shows how to Maintain Scroll Position from a Partial Page Update - Using Ajax |
erik little posted at Saturday, February 03, 2007 12:45 PM |
.gif) |
Original Article |
 |
|
|
Maintain scroll Position |
raj ron replied to erik little at Friday, February 16, 2007 8:16 AM |
Thanks the article is great....one question is ..
1. I wanted to use the smae stuff in a web page which is part of the master page, so I added the script under content provider. Second I removed of the serverstatus as it was not required in my case still I am not able to get the required functionality...any thing I am missing..
|
 |
|
|
One place to look is to make sure that you have the all |
erik little replied to raj ron at Friday, February 16, 2007 10:01 AM |
.gif) |
java script that works with the script manager at the bottom of the page.
If you already have this, please post you relevant code..
Erik
|
 |
|
|
Scroll Position and Multiple Updates |
Joe Reynolds replied to erik little at Tuesday, February 27, 2007 9:58 PM |
I have an aspx page that contrains multiple asp panels and within an ajax update panel. All works ok with ajax except a scroll position problem.
Let's say the user begins with Panel1 visible and is scrolling through a page. Then user clicks a link and Panel 1 is hidden and Panel 2 becomes visible. User then clicks on link that hides Panel 2 and makes Panel 1 visible again.
What I need to accomplish is to have Panel 1 return to the same scroll position it was in when the user originally clicked and hid it.
I use Strength Controls ScrollLock control for this in a non-ajax version of the page, but this control fails with ajax.
|
 |
|
|
BeginRequestHandler blocking code behind event execution |
Scott Bean replied to erik little at Wednesday, June 27, 2007 9:04 AM |
Erik -
Great solution. I've been trying to find an elegant solution to the maintain scroll position across ajax partial page updates for a while. But, in my case, it's not exactly working. Thought maybe you could tell me what I'm doing wrong.
I have a panel with a vertical scroll bar containing a radiobuttonlist. That is the panel that I need to maintain scroll position. That panel is contained in an ajax UpdatePanel which is contained in a Content page under a Master page. Since I don't have an <html> section in my content page, I placed the javascript at the end of the page just before the </asp:Content> tag. If I try to place the javascript after that tag, the compiler generates an error.
The problem that I am having is that the BeginRequestHandler is preventing my code behind events from be triggered. If I comment out the ...add_beginRequest... line of code, then my code behind events trigger. Do you know why and what I need to do the fix this?
Thanks!
Scott
|
 |
|
|
asp:panel tag vs. html tag |
Scott Bean replied to erik little at Wednesday, June 27, 2007 12:54 PM |
I found that my problem is that the javascript cannot find my asp:panel control using document.getElementById. Does anyone know to implement this javascript solution using the asp:panel control for scrolling? |
 |
|
|
Works Like a Charm |
Breaker Morant replied to erik little at Saturday, July 14, 2007 11:58 AM |
I had been scouring the Internet trying to find a non-complicated way to maintain scroll for several div/asp:panel elements on a page (i.e., without writing out pages of javascript). Your solution was the only one that worked! Thanks a bunch.
|
 |
|
|
Glad that i could help |
erik little replied to Breaker Morant at Saturday, July 14, 2007 12:14 PM |
.gif) |
Erik |
 |
|
|
My Solution with an ASP.NET Panel Control |
Breaker Morant replied to erik little at Saturday, July 14, 2007 12:23 PM |
This is how I made mine work, with only a little bit of javascript generated from a vb.net code-behind page. My pages have multiple panel controls, and it kept irking me that they would always scroll back to their respective tops after a postback. This is a modification of the solution presented earlier. All credit due to Mr. Little.
Private Shared csm As ClientScriptManager
Public Shared Sub load_scroll(ByVal p As Page, ByVal pnl As Panel)
csm = p.ClientScript
Dim sb As New StringBuilder
sb.AppendFormat("var scroll_top_{0};", pnl.ClientID)
sb.AppendLine()
sb.AppendFormat("var scroll_left_{0};", pnl.ClientID)
sb.AppendLine()
sb.AppendLine()
sb.AppendFormat("Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler_{0});", pnl.ClientID)
sb.AppendLine()
sb.AppendFormat("Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler_{0});", pnl.ClientID)
sb.AppendLine()
sb.AppendLine()
sb.AppendFormat("function BeginRequestHandler_{0}(sender, args)", pnl.ClientID)
sb.AppendLine()
sb.AppendLine("{")
sb.AppendFormat("var elem = document.getElementById('{0}');", pnl.ClientID)
sb.AppendLine()
sb.AppendFormat("scroll_top_{0} = elem.scrollTop;", pnl.ClientID)
sb.AppendLine()
sb.AppendFormat("scroll_left_{0} = elem.scrollLeft;", pnl.ClientID)
sb.AppendLine()
sb.AppendLine("}")
sb.AppendLine()
sb.AppendLine()
sb.AppendFormat("function EndRequestHandler_{0}(sender, args)", pnl.ClientID)
sb.AppendLine()
sb.AppendLine("{")
sb.AppendFormat("var elem = document.getElementById('{0}');", pnl.ClientID)
sb.AppendLine()
sb.AppendFormat("elem.scrollTop = scroll_top_{0};", pnl.ClientID)
sb.AppendLine()
sb.AppendFormat("elem.scrollLeft = scroll_left_{0};", pnl.ClientID)
sb.AppendLine()
sb.AppendLine("}")
sb.AppendLine()
If Not csm.IsStartupScriptRegistered(pnl.ClientID) Then
csm.RegisterStartupScript(p.GetType, pnl.ClientID, sb.ToString, True)
End If
End Sub
Please let me know if there any bugs with this.
|
 |
|
|
Everything looks great line for line |
erik little replied to Breaker Morant at Saturday, July 14, 2007 12:32 PM |
.gif) |
I am assuming that the java is getting appended to the bottom of the page, because you said it works....
i am going to add your code behind to my library....
Happy Coding!
Erik
|
 |
|
|
TreeView with AJAX |
Harish Chintapalli replied to erik little at Friday, October 05, 2007 10:13 AM |
Hi,
This article is great and is just what I am looking for.
I am trying to customize it so the TreeView has different expand and collapse images for the root as well for all the parent folders by using "OnTreeNodeExpanded" and "OnTreeNodeCollapsed" events. When I remove the "UpdatePanel" the TreeView works great. But when I add it "OnTreeNodeCollapsed" event fires even when I expand the root folder there by never showing full tree. Why does it behave so? What is that I am missing? Here is the code:
ASPX
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TagPrefix="asp" %>
<html>
<head>
<title>Populating TreeView Nodes On Demand Using Page Titles</title>
<link rel="stylesheet" href="/aspxtreme/shared/netdemos.css">
</head>
<body>
<div class="header"><h3>Populating TreeView Nodes On Demand Using Page Titles</h3></div>
<form id="Form1" runat="server">
<p id="msg">This example demonstrates using the TreeView <i>PopulateOnDemand</i> feature to programmatically create TreeNodes from the file system of the application that contains this sample. Here the TreeNodes display page titles instead of file names. </p>
<asp:ScriptManager runat="server"></asp:ScriptManager>
<asp:UpdatePanel runat="server">
<ContentTemplate>
<asp:treeview id="Treeview1" runat="server"
imageset="XPFileExplorer"
nodestyle-horizontalpadding=3
showlines="false"
expanddepth=1
onTreeNodePopulate="PopulateNode" OnTreeNodeExpanded="ExpandTreeNode" OnTreeNodeCollapsed="CollapseTreeNode">
<nodes>
<asp:treenode text="ASPXtreme" value="~/" populateondemand="true" />
</nodes>
</asp:treeview>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
Code behind:
using System;
using System.Web.UI.WebControls;
using System.IO;
using System.Text.RegularExpressions;
public partial class _Default : System.Web.UI.Page
{
protected void PopulateNode(Object source, TreeNodeEventArgs e)
{
TreeNode node = e.Node;
string fullPath = Request.MapPath(node.Value, Request.ApplicationPath, false);
// enumerate directories
string[] dirs = Directory.GetDirectories(fullPath);
foreach (string dir in dirs)
{
string virtualDir = node.Value.TrimEnd('/') + "/" + Path.GetFileName(dir);
if (virtualDir.IndexOf("admin") == -1)
{
TreeNode newNode = new TreeNode(Path.GetFileName(dir), virtualDir);
if (Directory.GetFiles(dir, "*.asp?").Length > 0 || Directory.GetDirectories(dir).Length > 0)
{
newNode.PopulateOnDemand = true;
node.ChildNodes.Add(newNode);
}
}
}
// enumerate files
string[] files = Directory.GetFiles(fullPath, "*.asp?");
foreach (string file in files)
{
if (file.IndexOf(".aspx.") == -1)
{
// open web page
System.Net.WebRequest request = System.Net.WebRequest.Create(file);
System.Net.WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string page = reader.ReadToEnd();
reader.Close();
response.Close();
// get page title
string title = Regex.Match(page, "<title>(?<title>[^<]+)</title>",
RegexOptions.IgnoreCase).Groups["title"].Value;
if (title != "")
{
TreeNode newNode = new TreeNode(title, Path.GetFileName(file));
newNode.NavigateUrl = "~/" + file.Substring(Request.PhysicalApplicationPath.Length);
node.ChildNodes.Add(newNode);
}
}
}
}
protected void ExpandTreeNode(object sender, TreeNodeEventArgs e)
{
if (e.Node.Parent == null)
e.Node.ImageUrl = "~/Images/RootFolderOpened.gif";
else
e.Node.ImageUrl = "~/Images/ParentFolderOpened.gif";
}
protected void CollapseTreeNode(object sender, TreeNodeEventArgs e)
{
if (e.Node.Parent == null)
e.Node.ImageUrl = "~/Images/RootFolderClosed.gif";
else
e.Node.ImageUrl = "~/Images/ParentFolderClosed.gif";
}
}
Appreciate your help and thanks,
Harish.
|
 |
|
|
You do not need my solution for your situation |
erik little replied to Harish Chintapalli at Sunday, October 07, 2007 8:52 PM |
.gif) |
All you need is just the updatepanel and the scriptmanager. I think you need to add some attributes to the scriptmanager for it to work.
Erik
|
 |
|
|
Ajax + TreeView = Wrong Scroll position |
Manuel Arbulú replied to erik little at Wednesday, October 17, 2007 1:32 PM |
Hi Erick, i've used your code inside an aspx page with an UpdatePanel and a Treeview, but the scroll position after postbacks with SelectedNode event is wrong.
Here is my code
aspx page:
<asp:UpdatePanel id="trvPanel" runat="server" UpdateMode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="mnuPrincipal" EventName="MenuItemClick" />
</Triggers>
<contenttemplate>
<asp:Panel ID="EmpresasPanel" runat="server" Height="650px" ScrollBars="Auto" BorderColor="#E0E0E0" BorderStyle="Solid" BorderWidth="1px">
<asp:TreeView id="trvEmpresas" runat="server" Width="376px" Height="600px" ShowLines="True" AutoGenerateDataBindings="False"OnSelectedNodeChanged="trvEmpresas_SelectedNodeChanged" NodeWrap="True">
<LevelStyles>
<asp:TreeNodeStyle Font-Underline="False" ImageUrl="~/images/empresa.gif" />
<asp:TreeNodeStyle Font-Underline="False" ImageUrl="~/images/grupo.gif" />
<asp:TreeNodeStyle Font-Underline="False" ImageUrl="~/images/user.gif" />
</LevelStyles>
<SelectedNodeStyle Font-Bold="True" />
<HoverNodeStyle BackColor="#E0E0E0" />
</asp:TreeView>
</asp:Panel>
</contenttemplate>
</asp:UpdatePanel>
And this is my codebehind method that puts the startup script
private void AddScrollScript()
{
string tvScript = "TreeViewScript";
Type cstype = this.GetType();
ClientScriptManager cs = Page.ClientScript;
if (!cs.IsStartupScriptRegistered(cstype, tvScript))
{
StringBuilder cstext = new StringBuilder();
cstext.AppendFormat("var scroll_top_{0};", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat("var scroll_left_{0};", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat("Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler_{0});", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat("Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler_{0});", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat("function BeginRequestHandler_{0}(sender, args)", trvPanel.ClientID);
cstext.AppendLine("{");
cstext.AppendFormat(" var elem = document.getElementById('{0}');", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat(" scroll_top_{0} = elem.scrollTop;", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat(" scroll_left_{0} = elem.scrollLeft;", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendLine("}");
cstext.AppendFormat("function EndRequestHandler_{0}(sender, args)", trvPanel.ClientID);
cstext.AppendLine("{");
cstext.AppendFormat(" var elem = document.getElementById('{0}');", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat(" elem.scrollTop = scroll_top_{0};", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendFormat(" elem.scrollLeft = scroll_left_{0};", trvPanel.ClientID);
cstext.AppendLine();
cstext.AppendLine("}");
cstext.AppendLine();
cs.RegisterStartupScript(cstype, tvScript, cstext.ToString(), true);
}
}
And in the generated page the startup script is well placed and well generated as the code i show you here
<script type="text/javascript">
<!--
var scroll_top_trvPanel;
var scroll_left_trvPanel;
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler_trvPanel);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler_trvPanel);
function BeginRequestHandler_trvPanel(sender, args){
var elem = document.getElementById('trvPanel');
scroll_top_trvPanel = elem.scrollTop;
scroll_left_trvPanel = elem.scrollLeft;
}
function EndRequestHandler_trvPanel(sender, args){
var elem = document.getElementById('trvPanel');
elem.scrollTop = scroll_top_trvPanel;
elem.scrollLeft = scroll_left_trvPanel;
}
WebForm_InitCallback();var trvEmpresas_Data = new Object();
trvEmpresas_Data.images = trvEmpresas_ImageArray;
trvEmpresas_Data.collapseToolTip = "Collapse {0}";
trvEmpresas_Data.expandToolTip = "Expand {0}";
trvEmpresas_Data.expandState = theForm.elements['trvEmpresas_ExpandState'];
trvEmpresas_Data.selectedNodeID = theForm.elements['trvEmpresas_SelectedNode'];
trvEmpresas_Data.hoverClass = 'trvEmpresas_9';
trvEmpresas_Data.hoverHyperLinkClass = '';
for (var i=0;i<19;i++) {
var preLoad = new Image();
if (trvEmpresas_ImageArray[i].length > 0)
preLoad.src = trvEmpresas_ImageArray[i];
}
trvEmpresas_Data.lastIndex = 17;
trvEmpresas_Data.populateLog = theForm.elements['trvEmpresas_PopulateLog'];
trvEmpresas_Data.treeViewID = 'trvEmpresas';
trvEmpresas_Data.name = 'trvEmpresas_Data';
theForm.oldSubmit = theForm.submit;
theForm.submit = WebForm_SaveScrollPositionSubmit;
theForm.oldOnSubmit = theForm.onsubmit;
theForm.onsubmit = WebForm_SaveScrollPositionOnSubmit;
Sys.Application.initialize();
// -->
</script>
The question is, What am i missing or what am I doing wrong?
Regards.
Manolo Arbulu
|
 |
|
|
A quick question |
Joe Fro replied to erik little at Sunday, October 21, 2007 8:50 PM |
Excellent solution, works great. I had to use var elem = document.getElementById('<%= this.gvPanel.ClientID %>');
elem.scrollTop = scrollTop;
to get access to my scrollable panel, I have a gridview within the panel so the gridview is scrollable. My problem is that I dont want the scroll position maintained on every post back, for example when i rebind my grid with new data, i want the scroll position to return to the top, you would think this would be as simple as adding this to my code behind:
StringBuilder sb = new StringBuilder();
sb.Append("<script language='javascript'>");
sb.Append("var thePanel = document.getElementById('<%= this.gvPanel.ClientID %>');");
sb.Append("thePanel.scrollTop = 0;");
sb.Append("</script>");
Type t = this.GetType();
if (!Page.ClientScript.IsClientScriptBlockRegistered(t, "ResetScroll"))
Page.ClientScript.RegisterClientScriptBlock(t, "ResetScroll", sb.ToString());
But for some reason there is a javascript error saying that thePanel is null, even though this same exact code works in the html. Is there an easier way of reseting the scroll position, or do you have any pointers? It would be much appreciated, cant figure this out. Thank you!
|
 |
|
|
The script manager that |
erik little replied to Joe Fro at Tuesday, October 23, 2007 12:39 PM |
.gif) |
is in your document is what maintains your ajax object model (Only the ScriptManager know where ALL the element's are located at all time's). So what you are trying to do here is introduce new java that your document can not process. When you return back from the server with this new javascript your document has no idea what is going on. As far as it know's nothing even happened.
That is why i used the add_beginRequest(BeginRequestHandler); What this statement is saying is "Hey ScriptManager, please let me know when you have received the beginning of a new request to the server" The programming model is not the same when using ajax. You must communicate directly when the ScriptManager from any area inside the update panel.
-----Your resources are all Here
http://ajax.asp.net/docs/ClientReference/Sys.WebForms/PageRequestManagerClass/default.aspx
****You can receive parameter's back from a request. I would recommend that you pass a Flag to the begin request so that when you receive that end request you can unpack the parameter and check if you need to reset the scroll position or not.
Look at this example:
http://asp.net/AJAX/Documentation/Live/ViewSample.aspx?sref=Sys.WebForms.PageRequestManager.endRequest/cs/default.aspx
You will noticed that in the part where the author is setting the endrequest that they are passing a String parameter.
<script type="text/javascript" language="javascript">
var divElem = 'AlertDiv';
var messageElem = 'AlertMessage';
var bodyTag = 'bodytag';
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
function ToggleAlertDiv(visString)
{
i
Your almost there .... you just need a Flag....
Just remember when you working with update panels that you can not directly program to any other part of the DOM after your first request to the server, because , your document has no idea about the NEW HTML.
****Ajax is tricky .... will have to code slow...
|
 |
|
|
thank you |
Joe Fro replied to erik little at Tuesday, October 23, 2007 12:18 PM |
Thank you for the quick reply, what you said makes sense and i will have a look at those links. And i completely agree, asp.net programming alone is fairly simple, when you add AJAX, it is a different thought process. Thanks!
Joe
|
 |
|
|
Scroll Maintain Updated for .NET 3.5 (VB.NET) |
Breaker Morant replied to erik little at Saturday, January 26, 2008 4:37 PM |
I modified my original code to use an shared function (returns string) and a .NET 3.5 extension method which extends an ASP.NET Panel control to maintain scroll position between postbacks. Make sure that the extension method is enclosed within a public-scoped Module for VB or public static class for C#. Once again, much respect due to Mr. Little.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
pnlProducts.LoadScroll(Me)
' or
ScriptManager.RegisterStartupScript(Me, Me.GetType, "scrollLoad", LoadScroll(pnlProducts), True)
End Sub
Public Module JavaScript
Public Function LoadScroll(ByVal panel As Panel) As String
Dim sb As New StringBuilder
sb.AppendFormat("var scrollTop{0};", panel.ClientID)
sb.AppendFormat("var scrollLeft{0};", panel.ClientID)
sb.AppendFormat("Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler{0});", panel.ClientID)
sb.AppendFormat("Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler{0});", panel.ClientID)
sb.AppendFormat("function BeginRequestHandler{0}(sender, args)", panel.ClientID)
sb.Append("{")
sb.AppendFormat("var elem=document.getElementById('{0}');", panel.ClientID)
sb.AppendFormat("scrollTop{0}=elem.scrollTop;", panel.ClientID)
sb.AppendFormat("scrollLeft{0}=elem.scrollLeft;", panel.ClientID)
sb.AppendLine("}")
sb.AppendFormat("function EndRequestHandler{0}(sender, args)", panel.ClientID)
sb.AppendLine("{")
sb.AppendFormat("var elem=document.getElementById('{0}');", panel.ClientID)
sb.AppendFormat("elem.scrollTop=scrollTop{0};", panel.ClientID)
sb.AppendFormat("elem.scrollLeft=scrollLeft{0};", panel.ClientID)
sb.AppendLine("}")
Return sb.ToString
End Function
<Extension()>
Public Sub LoadScroll(ByVal panel As Panel, ByVal page As Page)
Dim s As String = LoadScroll(panel)
If Not page.ClientScript.IsStartupScriptRegistered(panel.ClientID) Then
page.ClientScript.RegisterStartupScript(page.GetType, panel.ClientID, s, True)
End If
End Sub
End Module
|
 |
|
|
Master pages |
Kin Regtur replied to Breaker Morant at Thursday, July 31, 2008 5:52 AM |
Hello All,
I have used the next code in my content page.
<input id="scrollPos" type="hidden" />
<script type="text/javascript" language="javascript">
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
function EndRequestHandler(sender, args) {
var ScrollTopValue=document.getElementById("scrollPos").value;
document.getElementById('leftBar').scrollTop=ScrollTopValue;
}
function saveScrollPos(){
var elem = document.getElementById('leftBar');
scrollTop=elem.scrollTop;
document.getElementById("scrollPos").value= scrollTop;
}
</script>
</asp:Content>
this works in combination with masterpages.
Regards Bart
PS: don't forget <div id="leftBar" onscroll="saveScrollPos();" class="DV_ProductMenu">
|
 |
|
|
help? |
Eric Hunter replied to Breaker Morant at Tuesday, October 07, 2008 11:52 AM |
How do I get my asp:panel to reference your codebehind? I've been beating my head against my monitor for DAYS trying to find a solution for this. |
 |
|
|
Error? |
Eric Hunter replied to Breaker Morant at Tuesday, October 07, 2008 4:51 PM |
Breaker, This is working great! Thank you so much! The only problem I'm getting now is that when I scroll one of my panels an error comes up in the status bar that says:
Line: 74
Char: 1
Error: Object expected
Code:0
URL: ~my page's url
I check the source and on line 74 it has:
<script src="/Audit/ScriptResource.axd?d=8tHd7lmodXBiFUj-lZM0qNIZbCsI0VoojanxW7SE5MVD4jeaeiKTtcPqdDmacV5qVsBqS22-fVSBmFjlN5Ashw5LKopBiVdL8__WlepFViQ1&t=ffffffffcb92c336" type="text/javascript"></script>
any clues?
|
 |
|
|
NEVER MIND... |
Eric Hunter replied to Eric Hunter at Tuesday, October 07, 2008 5:06 PM |
I had a left over "onscroll=" in my tag for the panel. All is well. |
 |
|
|
Maintain position for multiple gridviews |
Colin Kennedy replied to erik little at Thursday, October 09, 2008 10:35 AM |
Eric,
This code works perfectly in my situation but I have a couple of pages with more than one gridview on the page. Each gridview has a div wrapper with a unique id. How can I apply your code to all of the gridviews on the page?
Colin Kennedy
|
 |