@bobnoordam

Maintaining vertical scroll position on an ASP.NET GridView across postbacks

Our mission

Here is what we want to archieve with a standard ASP.NET gridview;

  • We want scroll bars to scroll vertical in a potential long list
  • When a row is selected in the gridview, we want to maintain the scroll position in gridview. (de default behaviour would be for the grid to jump back up to the top of the list
  • When the user navigates away and comes back, we want the scroll state to reset to the top of the list.

In order to archive the above, we will need to take the following steps;

  • We need to wrap the gridview in a div, to enable scrolling
  • We need some javascript to save and restore the scroll state to and from a cookie
  • We need to reset the cookie if the load is not a PostBack

Wrapping the grid into a div

By wrapping the GridView into a div and setting the overflow property the gridview will be equiped with scrollbars, and we will have archived the first step. The div containing the view get’s it’s own id to be able to locate it later on when we need to determine or set the scroll position.

<div id="gridScroll" style="width: 100%; height: 800px; overflow: auto;">
    <asp:GridView ID="GridViewLongList" runat="server" CssClass="mGrid" AlternatingRowStyle-CssClass="alt"
        AutoGenerateColumns="False" OnSelectedIndexChanged="GridViewLongList_SelectedIndexChanged"
        <AlternatingRowStyle CssClass="alt"></AlternatingRowStyle>
        <Columns>
            <asp:CommandField ButtonType="Button" ShowSelectButton="True">
                <ControlStyle CssClass="buttonBase buttonSmall" />
            </asp:CommandField>
            <asp:BoundField DataField="SomeDate" DataFormatString="{0:d}" HeaderText="Date" />
            <asp:BoundField DataField="Field" HeaderText="Field" />
        </Columns>
        <SelectedRowStyle BackColor="#4c9ed9" />
    </asp:GridView>
</div>

The only important part here is the container div, all of the GridView stuff is the standard markup you can expect on any view.

Saving and restoring the state

Next up is a nice bunk of javascript. the window.onload function will read the document cookie and determine if the ypos position is set. If so, the container div is located and the scroll position is set. The second imporant function is the hook into the jQuery scroll function. Moving the scrollbar with the scrollwheel or the mouse will trigger this function, causing the Y position of the scrollbar to be saved to the cookie. The last section marked cookie handling does simply that, provide code to set and read a cookie. This typicaly goes into a more central position if you have many pages to implement the scrolling on.

<script type="text/javascript">
    /* restore scroll state on postback, reset cookie on initial load */
    window.onload = function () {
        var strPos = getCookie("yPos");
        document.getElementById("gridScroll").scrollTop = strPos;
    }
    /* Save state on scrolling */
    $("#gridScroll").scroll(function () {
        var intY = document.getElementById("gridScroll").scrollTop;
        setCookie("yPos", intY, 1, "/", "");
    });
    /* cookie handling */
    function getCookie(name) {
        var regexp = new RegExp("(?:^" + name + "|;\s*" + name + ")=(.*?)(?:;|$)", "g");
        var result = regexp.exec(document.cookie);
        return (result === null) ? null : result[1];
    }
    function setCookie(name, value) {
        var cookie = name + "=" + escape(value) + ";";
        var path = "/";
        var domain = "";
        var expires = new Date(new Date().getTime() + 3600000);
        cookie += "expires=" + expires.toGMTString() + ";";
        cookie += "path=" + path + ";";
        cookie += "domain=" + domain + ";";
        document.cookie = cookie;
    }
</script>

Resetting state

Now we are bascialy done, but one nasty behaviour remains; when we navigate away from the page and return later, the cookie value is still present and we will jump to the scroll position. Most likely you dont want that. So we will use a small bit of servside code to detect if we are dealing with an initial load or with a PostBack, and reset the cookie value if the load is initial.

if (!IsPostBack) {
    if (Response.Cookies["yPos"] != null) {
        Response.Cookies["yPos"].Value = "0";
    }
    BindSomeDataStuffToGrid();
}

All set. The grid will start at the topmost position on initial load, and maintain state on postbacks ! If you have a grid that also needs horizontal scrolling, it is now quite easy to also add the X coordinate to the code above, and maintain both horizontal and vertical scroll positions.

And then ..

From here on the basics are functional, and you can finetune to your requirements. In my case i had many pages with roughly the same layout with the grid always at the bottom of the page. That enabled me to do something like below where the container div no longer contains markup and to auto-size the grid to always fit nicely into the available space.

<script type="text/javascript">
       $(document).ready(function () {
           /* auto-size the grid height to the free space */
           var screenHeight = $(window).height();
           var offset = $("#gridScroll").offset().top;
           var size = screenHeight - offset - 25;
           document.getElementById("gridScroll").setAttribute("style", "width: 100%; overflow: auto; height:" + size + "px");
           /* restore scroll state on postback */
           var strPos = getCookie("yPos");
           document.getElementById("gridScroll").scrollTop = strPos;
       });
       /* Save state on scrolling */
       $("#gridScroll").scroll(function() {
           var intY = document.getElementById("gridScroll").scrollTop;
           setCookie("yPos", intY, 1, "/", "");
       });
   </