MBC Computer Solutions Ltd.

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Monday, 10 September 2007

Persisting the scroll position of child DIV's using MS AJAX

Posted on 14:20 by Unknown

  • Download source and demo project - 29.5 Kb


Introduction

While the ScriptManager and UpdatePanel found in Microsoft AJAX do a good job of persisting your pages scroll position during partial post back operations, you might be surprised to find out the same is not for scrollable child DIV's contained within an UpdatePanel.

The PersistentScrollPosition control presented in this article seeks to remedy this issue using a client-side behavior and ASP.NET server control implemented using Microsoft AJAX.


Background

While it is certainly not my intention to review the internals of the UpdatePanel and PageRequestManager or implementing any of the client-side components (Sys.Component, Sys.UI,Behavior, Sys.UI.Control), a quick understanding can go a long way into understanding and resolving this particular problem. There are two key items to keep in mind for this control:


  1. Client-side components are disposed of and recreated during the partial post back lifecycle so you can't use the controls instance to store any data you need to survive this.
  2. The HTML output of the UpdatePanel is completely replaced during a partial post back (assuming it was triggered) through the innerHTML property, which is why the scroll position problem exists in the first place.


Using the code

For those who just want the solution, using the code is straight forward. The control has one property you need to set named ControlToPersist. This is a string which takes the ID of the server-side container DIV (it must have runat="server").

<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode="always">
<ContentTemplate>
<asp:Button runat="server" ID="btnPostBack" Text="Post Back" OnClick="btnPostBack_Click" />
<br />
<div style="width:590px;height:400px;overflow-y:scroll;overflow-x:hidden;" runat="server" id="persistMe">
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit...</p>

</div>
<mbc:PersistentScrollPosition runat="server" ID="psf1" ControlToPersist="persistMe" />
</ContentTemplate>
</asp:UpdatePanel>


Building the Control

The control consists of two parts, both of which for the most part are very "cookie cutter". On the server side, we inhert from Control and implement IScriptControl and INamingContainer and create a HiddenField during initialization to store our scroll position in between partial post backs.


public class PersistentScrollPosition : Control, IScriptControl, INamingContainer
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);

// Create hidden control for storage
storage = new HiddenField();
storage.ID = "storage";
Controls.Add(storage);
}}



When creating the script descriptors for the client-side initiation, we pass through the scrollable DIV's ClientID as the controls ElementID and we pass in a reference to the HiddenField's DOM element using the AddElementProprety method of the ScriptComponentDescriptor class.





public IEnumerable<scriptdescriptor /> GetScriptDescriptors()
{
ScriptComponentDescriptor scd =
new ScriptBehaviorDescriptor("Mbccs.WebControls.PersistentScrollPosition", Control.ClientID);
scd.AddElementProperty("storage", storage.ClientID);
yield return scd;
}


On the client-side, the control inherits from the Sys.UI.Behavior base class. Upon control intialization, it hooks into two events:
The scroll DOM event of the DIV, and the EndRequest event of the Sys.WebForms.PageRequestManager class. The EndRequest event is when the scroll state is restored, but I'll get to that shortly.


    initialize : function() {
Mbccs.WebControls.PersistentScrollPosition.callBaseMethod(this, 'initialize');

this._scrollDelegate = Function.createDelegate(this, this._onScroll);
this._endRequestDelegate = Function.createDelegate(this, this._onEndRequest);

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(this._endRequestDelegate);

$addHandler(this.get_element(), 'scroll', this._scrollDelegate);

}



When the DIV is scrolled, the x,y scroll position is serialized and stored in the HiddenField server-side control we created earlier.


_onScroll : function(e) {            
this._storage.value =
Sys.Serialization.JavaScriptSerializer.serialize(this._getScrollPosition());
},

_getScrollPosition : function() {
var el = this.get_element();

if (el) {
return {
x: el.scrollLeft 0,
y: el.scrollTop 0
};
}
}


To prevent null's from floating around, the x,y coordinates are coerced into 0's if either the scrollLeft or scrollTop properties are null.

The EndRequest event is fired when "an asyncronous postback is finished and control has been returned to the browser," (http://asp.net/AJAX/Documentation/Live/ClientReference/Sys.WebForms/PageRequestManagerClass/default.aspx) so it's a perfect time to restore our scroll state.



_onEndRequest : function(sender, args) {        
var o = null;
if(this._storage.value !== '')
o = Sys.Serialization.JavaScriptSerializer.deserialize(this._storage.value);

if (o) {
var el = this.get_element();
el.scrollLeft = o.x;
el.scrollTop = o.y;
this._storage.value = '';
}
}

The dispose method isn't usually a place of any particular interest, but its worthy of noting that it you need to unhook the DIV's scroll event prior to calling dispose on the base class.

dispose : function() {            
$removeHandler(this.get_element(), 'scroll', this._scrollDelegate);

Mbccs.WebControls.PersistentScrollPosition.callBaseMethod(this, 'dispose');

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.remove_endRequest(this._endRequestDelegate);

delete this._endRequestDelegate;
delete this._scrollDelegate;
}
Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest
Posted in | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • How to change the temperature scale on a Honeywell T6575 Thermostat
    [The complete documentation can be found at http://customer.honeywell.ca/techlit/pdf/95c-00000s/95c-10897.pdf ]   This was bugging me fo...
  • C# – Converting IP’s to Numbers and Numbers to IP’s in 2 lines of code
    I don’t know why everywhere I searched had such complex implementation of this, but converting from a dotted IP to a number (integer) and ba...
  • Why does iTunes setup need to close Outlook?!
    Everytime I update iTunes I remember why I left it so long - the install process is quite annoying! Can someone please explain to me why it...
  • Mac OSX 10.5.2 Freezing Intermittently
    I've been having an issue with my MacBook (you know, that computer I hide under my desk most of the time) where intermittently, the UI w...
  • ScottGu’s Color Scheme for Visual Studio 2010
    ScottGu was nice enough to provide the world with his awesome Visual Studio 2008 color scheme.  I’ve been using this for many years now an...
  • Don’t forget about the defer attribute for non-essential external scripts
    I was recently reviewing a customers eCommerce site and I noticed that the “Please Wait” page that occurs after completing an order but befo...
  • Windows Search 4.0 Released .....and searching finally works!
    I've been dealing with Outlook 2007's search problems since installing it way back then.  Most frequently, I'd search a keyword;...
  • Recursively finding controls - where to start?
    I love hearing about bugs and problems in components I have authored.  Most people hate hearing about bugs (I assume because they like to th...
  • Dell PowerEdge & Broadcom Issues
    For some time now we've been experiencing a problem across the board with Dell PowerEdge 2900/2950 servers equipped with Broadcom Gigabi...
  • Popup Window Manager
    I was just reading a post by Rick Strahl about managing popup windows in the browser.  I actually authored a mini popup window manager a wh...

Blog Archive

  • ►  2012 (1)
    • ►  February (1)
  • ►  2010 (1)
    • ►  April (1)
  • ►  2009 (7)
    • ►  December (1)
    • ►  November (1)
    • ►  October (1)
    • ►  July (1)
    • ►  April (2)
    • ►  February (1)
  • ►  2008 (36)
    • ►  November (3)
    • ►  October (2)
    • ►  September (1)
    • ►  August (1)
    • ►  July (2)
    • ►  June (6)
    • ►  May (4)
    • ►  April (1)
    • ►  March (4)
    • ►  February (7)
    • ►  January (5)
  • ▼  2007 (35)
    • ►  December (1)
    • ►  November (9)
    • ►  October (3)
    • ▼  September (6)
      • JavaScript Gotcha: Check for undefined before null
      • JavaScript Hashtable based on MS AJAX
      • Office 2003 SP3 Released
      • Why does iTunes setup need to close Outlook?!
      • Persisting the scroll position of child DIV's usin...
      • Strongly typed URL's for ASP.NET
    • ►  August (7)
    • ►  July (9)
  • ►  2006 (3)
    • ►  May (3)
Powered by Blogger.

About Me

Unknown
View my complete profile