Wednesday, 20 November 2013

Column freeze in GridView

http://gridviewscroll.aspcity.idv.tw/
http://gridviewscroll.aspcity.idv.tw/Demo.aspx


In aspx:

asp:GridView ID="gvMain" runat="server" Width="100%" 
    AutoGenerateColumns="False" GridLines="None" OnRowCreated="gvMain_RowCreated"> 
    <Columns> 
        <asp:BoundField HeaderText="ProductID" DataField="ProductID" ItemStyle-BackColor="#EEEEEE" /> 
        <asp:BoundField HeaderText="Name" DataField="Name" ItemStyle-BackColor="#EEEEEE" /> 
        <asp:BoundField HeaderText="Number" DataField="ProductNumber" ItemStyle-BackColor="#EEEEEE" /> 
        <asp:BoundField HeaderText="ReorderPoint" DataField="ReorderPoint" /> 
        <asp:BoundField HeaderText="Weight" DataField="Weight" /> 
        <asp:BoundField HeaderText="StandardCost" DataField="StandardCost" /> 
        <asp:BoundField HeaderText="ListPrice" DataField="ListPrice" /> 
        <asp:BoundField HeaderText="SafetyStockLevel" DataField="SafetyStockLevel" /> 
        <asp:BoundField HeaderText="SellStartDate" DataField="SellStartDate" /> 
    </Columns> 
    <HeaderStyle CssClass="GridviewScrollC1Header" /> 
    <RowStyle CssClass="GridviewScrollC1Item" /> 
    <PagerStyle CssClass="GridviewScrollC1Pager" /> 
</asp:GridView> 
 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script> 
<script type="text/javascript" src="../Scripts/gridviewScroll.min.js"></script> 
<script type="text/javascript"> 
    $(document).ready(function () { 
        gridviewScroll(); 
    }); 
 
    function gridviewScroll() { 
        $('#<%=gvMain.ClientID%>').gridviewScroll({ 
            width: 700, 
            height: 330, 
            freezesize: 2, 
            arrowsize: 30, 
            varrowtopimg: "Images/arrowvt.png", 
            varrowbottomimg: "Images/arrowvb.png", 
            harrowleftimg: "Images/arrowhl.png", 
            harrowrightimg: "Images/arrowhr.png", 
            headerrowcount: 2 
        }); 
    } 
</script>
///************

in CSS:

.GridviewScrollHeader TH, .GridviewScrollHeader TD
{
    padding: 5px;
    font-weight: bold;
    white-space: nowrap;
    border-right: 1px solid #AAAAAA;
    border-bottom: 1px solid #AAAAAA;
    background-color: #EFEFEF;
    vertical-align: bottom;
    text-align: left;
}
.GridviewScrollItem TD
{
    padding: 5px;
    white-space: nowrap;
    border-right: 1px solid #AAAAAA;
    border-bottom: 1px solid #AAAAAA;
    background-color: #FFFFFF;
}
.GridviewScrollItem .Freeze
{
    background-color: #EFEFEF;
}
.GridviewScrollItemHover TD
{
    padding: 5px;
    white-space: nowrap;
    border-right: 1px solid #AAAAAA;
    border-bottom: 1px solid #AAAAAA;
    background-color: #CCCCCC;
    cursor: pointer;
}
.GridviewScrollItemHover .Freeze
{
    background-color: #CCCCCC;
}
.GridviewScrollItemSelected TD
{
    padding: 5px;
    white-space: nowrap;
    border-right: 1px solid #AAAAAA;
    border-bottom: 1px solid #AAAAAA;
    background-color: #999999;
    color: #FFFFFF;
}
.GridviewScrollItemSelected .Freeze
{
    background-color: #999999;
}
.GridviewScrollPager 
{
    border-top: 1px solid #AAAAAA;
    background-color: #FFFFFF;
}
.GridviewScrollPager TD
{
    padding-top: 3px;
    font-size: 14px;
    padding-left: 5px;
    padding-right: 5px;
}
.GridviewScrollPager A
{
    color: #666666;
}

.GridviewScrollPager SPAN
{
    font-size: 16px;
    font-weight: bold;
}
/**************

jquery

/*
 * GridViewScroll with jQuery v0.9.6.8
 * http://gridviewscroll.aspcity.idv.tw/

 * Copyright (c) 2012 Likol Lee
 * Released under the MIT license

 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 

 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 * THE SOFTWARE.
 */
(function (n) { jQuery.fn.extend({ gridviewScroll: function (t) { function dt(n, t) { hr(n, t), cr(n, t), lr(n, t) } function hr(t, i) { t.find("input").each(function () { var r = n(this)[0].type, u; if (r == "checkbox" || r == "radio" || r == "text") { var t = n(this), f = t[0].id.replace("_Copy", ""), e = t[0].name.replace("_Copy", ""); t[0].name = e + "_" + i, t[0].id = f + "_" + i, u = n("#" + f); switch (r) { case "checkbox": case "radio": t.off("change"), t.change(function () { var t = n(this).is(":checked"); u.attr("checked", t) }); break; case "text": t.change(function () { var t = n(this).val(); u.val(t) }) } } }) } function cr(t, i) { t.find("select").each(function () { var t = n(this), u = t[0].id.replace("_Copy", ""), f = t[0].name.replace("_Copy", ""), r; t[0].name = f + "_" + i, t[0].id = u + "_" + i, r = n("#" + u), t.off("change"), t.prop("selectedIndex", r[0].selectedIndex), t.change(function () { var n = this.selectedIndex; r.prop("selectedIndex", n) }) }) } function lr(t, i) { t.find("textarea").each(function () { var t = n(this), r = t[0].id.replace("_Copy", ""), f = t[0].name.replace("_Copy", ""), u; t[0].name = f + "_" + i, t[0].id = r + "_" + i, u = n("#" + r), t.off("change"), t.change(function () { var t = n(this).val(); u.val(t) }) }) } function ar() { var g = a.attr("id") + "Freeze", ot, o, st, nt, w, b, ht, d, ct, s, at, e, h, ft, c, v, t; for (vr(), document.getElementById(g) ? et = n("#" + g) : (et = n(a[0].cloneNode(!1)), et.attr("id", g), et.css({ position: "absolute", width: "", height: "100%", top: 0, left: 0, zIndex: ur }), o = n(ut[0].cloneNode(!1)), ot = o.attr("id") + "Freeze", o.attr("id", ot), o.css({ width: "", height: "100%" }), et[0].appendChild(o[0]), rt[0].appendChild(et[0])), o = et.children().eq(0) ; o[0].hasChildNodes() ;) o[0].removeChild(o[0].lastChild); for (st = document.createElement("TBODY"), o[0].appendChild(st), nt = ut.children().eq(0), w = k[0].length, e = 0; e < i.headerrowcount; e++) { for (h = nt[0].rows[e].cloneNode(!1), t = 0; t < w; t++) (b = k[e][t], b != "RS" && b != "CS") && (ht = parseInt(b.split(":")[1]), t < lt && (v = nt[0].rows[e].cells[ht].cloneNode(!0), h.appendChild(v))); dt(n(h), "freezeheader"), o[0].childNodes[0].appendChild(h) } for (et[0].style.display = "", d = r.attr("id") + "Freeze", document.getElementById(d) ? (p = n("#" + d), p[0].style.height = r[0].style.height, p.scrollTop(0)) : (p = n(r[0].cloneNode(!1)), p.attr("id", d), p.css({ position: "absolute", width: "", top: 0, left: 0, zIndex: ur, display: "none" }), s = n(u[0].cloneNode(!1)), ct = s.attr("id") + "Freeze", s.attr("id", ct), s[0].style.width = "", p[0].appendChild(s[0]), f[0].appendChild(p[0])), s = p.children().eq(0) ; s[0].hasChildNodes() ;) s[0].removeChild(s[0].lastChild); at = document.createElement("TBODY"), s[0].appendChild(at); var vt = l[0].rows.length, tt = lt, it = tt, y = []; for (e = 0; e < vt; e++) { if (h = l[0].rows[e].cloneNode(!1), e < i.headerrowcount) h.style.display = "none"; else { for (t = 0; t < w; t++) ft = l[0].rows[e].cells[t], t < tt && (c = ft.rowSpan, c = c ? parseInt(c) : 1, c != 1 && (it--, y[t] = c), v = ft.cloneNode(!0), h.appendChild(v)); for (t = 0; t < w; t++) y[t] && y[t] > 0 && (y[t]--, y[t] == 0 && it++); tt = it, dt(n(h), "freezeitem") } s[0].childNodes[0].appendChild(h) } p[0].style.display = "" } function vr() { var r = 0, n, t; for (lt = 0, n = 0; n < k[0].length; n++) { if (t = k[0][n], t == "RS" || t == "CS") { lt++; continue } if (i.freezesize == r) return lt; r++, lt++ } } function yr() { var e, r, t, u, o, f, n; for (ni = [], e = l.children().length, n = i.headerrowcount; n < e; n++) r = l[0].rows[n], t = r.cells[0], t.style.height = "", u = t.childNodes[0], u && u.className == "GridCellDiv" || (u = nr(t)); for (o = 1, n = i.headerrowcount; n < e; n++) { var r = l[0].rows[n], t = r.cells[0], u = t.childNodes[0], s = 0; o == 1 ? (f = t.rowSpan, f = f ? parseInt(f) : 1, o = f, s = gi(r)) : o--, s = gi(r), ni[n] = s } for (n = i.headerrowcount; n < e; n++) { var r = l[0].rows[n], t = r.cells[0], u = t.childNodes[0]; ni[n] != 0 && (t.style.height = ni[n] + "px") } } function gi(t) { var u = 0, i = t.cells[0], r = n(i), f = parseInt(r.css("padding-top")), e = parseInt(r.css("padding-bottom")); return (u = i.offsetHeight - f - e) + 0 } function pr() { var rt = u.attr("id") + "VerticalRail", r, ut, l, a, t, v, p, k, tt; document.getElementById(rt) ? c = n("#" + rt) : (c = n(it).css({ background: i.railcolor, width: i.railsize + "px", position: "absolute", zIndex: ot }), c.attr("id", rt), f.append(c), t = { right: 0 }, c.css(t), c.mousedown(function (t) { clearInterval(h); var i = n(this).offset(), r = t.clientY - i.top + n(document).scrollTop(), u = s.offset().top - i.top, f = s.height() + u; r < u && y(-1, !1, !0), r > f && y(1, !1, !0), h = window.setInterval(function () { var r = t.clientY - i.top + n(document).scrollTop(), u = s.offset().top - i.top, f = s.height() + u; r < u && y(-1, !1, !0), r > f && y(1, !1, !0) }, 200) }), c.mouseup(function () { clearInterval(h) }), c.mouseout(function () { clearInterval(h) })), r = u.attr("id") + "VerticalBar", document.getElementById(r) ? s = n("#" + r) : (s = n(it).css({ background: i.barcolor, width: i.barsize + "px", position: "absolute", zIndex: ot }), s.attr("id", r), ut = { right: (i.railsize - i.barsize) / 2 }, s.css(ut), f.append(s), s.draggable({ axis: "y", containment: c, start: function () { n(this).css({ backgroundColor: i.barhovercolor }) }, stop: function () { n(this).css({ backgroundColor: i.barcolor }) }, drag: function () { y(0, !1) } })), l = u.attr("id") + "Vertical_TIMG", document.getElementById(l) ? w = n("#" + l) : (w = n(gt).css({ height: i.arrowsize, position: "absolute", zIndex: ot, top: 0 }), w.attr("id", l), w.attr("src", i.varrowtopimg), f.append(w), t = { right: 0 }, w.css(t), w.mousedown(function () { clearInterval(h), y(-1, !1, !0), h = window.setInterval(function () { y(-1, !1, !0) }, 200) }), w.mouseup(function () { clearInterval(h) }), w.mouseout(function () { clearInterval(h) })), a = u.attr("id") + "Vertical_BIMG", document.getElementById(a) ? b = n("#" + a) : (b = n(gt).css({ height: i.arrowsize, position: "absolute", zIndex: ot }), b.attr("id", a), b.attr("src", i.varrowbottomimg), f.append(b), t = { right: 0 }, b.css(t), b.mousedown(function () { clearInterval(h), y(1, !1, !0), h = window.setInterval(function () { y(1, !1, !0) }, 200) }), b.mouseup(function () { clearInterval(h) }), b.mouseout(function () { clearInterval(h) })), v = u.attr("id") + "HorizontalRail", document.getElementById(v) ? e = n("#" + v) : (e = n(it).css({ background: i.railcolor, height: i.railsize + "px", position: "absolute", zIndex: ot }), e.attr("id", v), f.append(e), e.mousedown(function (t) { clearInterval(h); var i = n(this).offset(), r = t.clientX - i.left + n(document).scrollLeft(), u = o.offset().left - i.left, f = o.width() + u; r < u && d(-1, !0), r > f && d(1, !0), h = window.setInterval(function () { var r = t.clientX - i.left + n(document).scrollLeft(), u = o.offset().left - i.left, f = o.width() + u; r < u && d(-1, !0), r > f && d(1, !0) }, 200) }), e.mouseup(function () { clearInterval(h) }), e.mouseout(function () { clearInterval(h) })), p = u.attr("id") + "HorizontalBar", document.getElementById(p) ? o = n("#" + p) : (o = n(it).css({ background: i.barcolor, height: i.barsize + "px", position: "absolute", zIndex: ot }), o.attr("id", p), f.append(o), o.draggable({ axis: "x", containment: e, start: function () { n(this).css({ backgroundColor: i.barhovercolor }) }, stop: function () { n(this).css({ backgroundColor: i.barcolor }) }, drag: function () { d() } })), k = u.attr("id") + "Horizontal_LIMG", document.getElementById(k) ? g = n("#" + k) : (g = n(gt).css({ width: i.arrowsize, position: "absolute", zIndex: ot, top: 0 }), g.attr("id", k), g.attr("src", i.harrowleftimg), f.append(g), g.mousedown(function () { clearInterval(h), d(-1, !0), h = window.setInterval(function () { d(-1, !0) }, 200) }), g.mouseup(function () { clearInterval(h) }), g.mouseout(function () { clearInterval(h) })), tt = u.attr("id") + "Horizontal_RIMG", document.getElementById(tt) ? nt = n("#" + tt) : (nt = n(gt).css({ width: i.arrowsize, position: "absolute", zIndex: ot }), nt.attr("id", tt), nt.attr("src", i.harrowrightimg), f.append(nt), nt.mousedown(function () { clearInterval(h), d(1, !0), h = window.setInterval(function () { d(1, !0) }, 200) }), nt.mouseup(function () { clearInterval(h) }), nt.mouseout(function () { clearInterval(h) })) } function wr() { var n, t; c.css({ height: f.outerHeight() - i.railsize - i.arrowsize * 2, top: i.arrowsize }), s.css({ top: i.arrowsize }), e.css({ width: f.outerWidth() - i.railsize - i.arrowsize * 2, top: f.outerHeight() - i.railsize, left: i.arrowsize }), o.css({ top: f.outerHeight() - (i.railsize + i.barsize) / 2, left: i.arrowsize }), n = Math.max(r.outerHeight() / r[0].scrollHeight * c.outerHeight(), i.minscrollbarsize), s.css({ height: n + "px" }), t = Math.max(r.outerWidth() / r[0].scrollWidth * e.outerWidth(), i.minscrollbarsize), o.css({ width: t + "px" }), n + i.arrowsize * 2 >= r.outerHeight() || i.verticalbar == "hidden" ? (c.hide(), s.hide(), w.hide(), b.hide(), li = !0) : (c.show(), s.show(), w.show(), b.show()), t + i.arrowsize * 2 >= r.outerWidth() || i.horizontalbar == "hidden" ? (e.hide(), o.hide(), g.hide(), nt.hide()) : (e.show(), o.show(), g.show(), nt.show()), c.is(":hidden") && (rt.css({ width: v }), a.css({ width: v }), f.css({ width: v }), r.css({ width: v }), e.css({ width: v - i.arrowsize * 2 }), t = Math.max(r.outerWidth() / r[0].scrollWidth * e.outerWidth(), i.minscrollbarsize), o.css({ width: t + "px" }), e.css({ top: f.height() - i.railsize }), o.css({ top: f.height() - (i.railsize + i.barsize) / 2 })), e.is(":hidden") ? (c.is(":hidden") ? (f.css({ height: u.height() }), r.css({ height: u.height() }), ft[0].style.height = "") : (f.css({ height: ht }), r.css({ height: ht })), c.css({ height: ht - i.arrowsize * 2 }), n = Math.max(r.outerHeight() / r[0].scrollHeight * c.outerHeight(), i.minscrollbarsize), s.css({ height: n + "px" })) : c.is(":hidden") && (i.height == -1 || at > u.height() + vi - i.railsize) && (f.css({ height: u.height() + i.railsize }), r.css({ height: u.height() }), ft[0].style.height = "", e.css({ top: f.height() - i.railsize }), o.css({ top: f.height() - (i.railsize + i.barsize) / 2 })), w.css({ top: 0 }), b.css({ top: c.outerHeight() + i.arrowsize }), g.css({ top: r.outerHeight() }), nt.css({ top: r.outerHeight(), left: e.outerWidth() + i.arrowsize }), i.arrowsize == 0 && (w.hide(), b.hide(), g.hide(), nt.hide()) } function y(n, t, u, f) { var o = n, l = r.outerHeight() - s.outerHeight() - i.arrowsize, h, a; if (t || u ? (h = 0, h = t ? n * parseInt(i.wheelstep) / 100 : n * .8, o = parseInt(s.css("top")) + h * r.outerHeight() / r[0].scrollHeight * c.outerHeight(), o = Math.min(Math.max(o, i.arrowsize), l), s.css({ top: o + "px" })) : f && (o = Math.min(Math.max(o, i.arrowsize), l), s.css({ top: o + "px" })), typeof i.onScrollVertical == "function") i.onScrollVertical(parseInt(s.css("top")) - i.arrowsize); a = (parseInt(s.css("top")) - i.arrowsize) / (c.outerHeight() - s.outerHeight()), o = a * (r[0].scrollHeight - r.outerHeight()), r.scrollTop(o), i.freezesize != 0 && e[0].style.display != "none" && (o + r.outerHeight() > r[0].scrollHeight && (o = r[0].scrollHeight - r.outerHeight()), p.scrollTop(o)) } function d(n, t, u) { var f = n, h, s, c; if (t ? (h = n * .8, f = parseInt(o.css("left")) + h * r.outerWidth() / r[0].scrollWidth * e.outerWidth(), s = r.outerWidth() - o.outerWidth() - i.arrowsize, f = Math.min(Math.max(f, i.arrowsize), s), o.css({ left: f + "px" })) : u && (s = r.outerWidth() - o.outerWidth() - i.arrowsize, f = Math.min(Math.max(f, i.arrowsize), s), o.css({ left: f + "px" })), typeof i.onScrollHorizontal == "function") i.onScrollHorizontal(parseInt(o.css("left")) - i.arrowsize); c = (parseInt(o.css("left")) - i.arrowsize) / (e.outerWidth() - o.outerWidth()), f = c * (r[0].scrollWidth - r.outerWidth()), f + a.outerWidth() > a[0].scrollWidth && (f = a[0].scrollWidth - a.outerWidth()), r.scrollLeft(f), a.scrollLeft(f) } function br() { var c = st[0].cells.length, a, v, y, o, s, r, e, h, w, p, t; if (ct.show(), i.headerrowcount > 1) for (t = 1; t < i.headerrowcount; t++) l.children().eq(t).show(); for (t = 0; t < c; t++) st[0].cells[t].childNodes[0].style.width = ""; for (a = 1, v = u[0].offsetWidth, v < f[0].offsetWidth && (a = 0), y = ut.children().eq(0), kr(), ai = [], pt = [], t = 0; t < c; t++) pt[t] = !1, o = st[0].cells[t], r = o.childNodes[0].offsetWidth + a, o.style.width && o.style.width != "auto" && (s = o.style.width, s.indexOf("%") == -1 ? r = parseInt(o.style.width) : (s = s.replace("%", ""), r = parseInt(v * (s / 100)), pt[t] = !0)), a == 0 && t == c - 1 && r--, ai[t] = r; for (t = 0; t < c; t++) { if (r = ai[t], st[0].cells[t].childNodes[0].style.width = r + "px", pt[t]) for (n(st[0].cells[t]).css("width", r + "px"), e = i.headerrowcount; e < u[0].rows.length; e++) n(u[0].rows[e].cells[t]).css("width", r + "px"); for (e = 0; e < i.headerrowcount; e++) (h = k[e][t], h != "RS" && h != "CS") && (w = h.split(":")[0], w != "N") && (p = h.split(":")[1], pt[t] && n(y[0].rows[e].cells[p]).css("width", r + "px"), y[0].rows[e].cells[p].childNodes[0].style.width = r + "px") } if (ct.hide(), i.headerrowcount > 1) for (t = 1; t < i.headerrowcount; t++) l.children().eq(t).hide() } function kr() { for (var s = st[0].cells.length, e = [], t, o, f, u, r, n = 0; n < i.headerrowcount; n++) k[n] = [], e[n] = 0; for (t = 0; t < s; t++) for (n = 0; n < i.headerrowcount; n++) if (k[n][t] != "RS" && k[n][t] != "CS") { if (o = l.children().eq(n).children().eq(e[n]), e[n]++, f = o.attr("rowspan"), u = o.attr("colspan"), f = f ? parseInt(f) : 1, u = u ? parseInt(u) : 1, f != 1) for (r = n; r < f; r++) k[r][t] = "RS"; if (u != 1) { for (r = 1; r < u; r++) k[n][r + t] = "CS"; k[n][t] = "N:" + (e[n] - 1) } u == 1 && (k[n][t] = "Y:" + (e[n] - 1)) } return k } function dr() { var r, t; for (u[0].style.display = "none", r = i.headerrowcount + 1, t = 0; t < r; t++) l.children().eq(t).find("td, th").each(function () { var t = n(this)[0]; t.childNodes[0] && (t.childNodes[0].tagName == "DIV" || t.childNodes[0].className == "GridCellDiv") || nr(t) }); u[0].style.display = "" } function nr(n) { var t = document.createElement("DIV"); for (t.className = "GridCellDiv"; n.hasChildNodes() ;) t.appendChild(n.firstChild); return n.appendChild(t), t } function gr() { var p = u.attr("id") + "Wrapper", r = n("#" + p), a, h, f, c, t, v, i, y, e, o, s; if (r[0] && (a = u[0].id + "Copy", h = document.getElementById(a), h)) { for (r[0].parentNode.insertBefore(u[0], r[0]), f = h.rows.length, t = 0; t < f; t++) u[0].rows[t].style.display = ""; for (l = u.children().eq(0), nu(f), c = u[0].rows.length, t = f; t < c; t++) v = l[0].rows[t], i = v.cells[0], i.style.height = "", i.childNodes[0] && i.childNodes[0].tagName == "DIV" && i.childNodes[0].className == "GridCellDiv" && tr(i); y = u[0].id + "PagerBottom", e = document.getElementById(y), e && (o = document.createElement("TD"), o.colSpan = u[0].rows[c - 2].cells.length, o.appendChild(e.childNodes[0]), s = document.createElement("TR"), s.className = e.className, s.appendChild(o), n(s).appendTo(u.children().eq(0))), r.remove() } } function nu(t) { for (var r = t + 1, i = 0; i < r; i++) l.children().eq(i).find("td, th").each(function () { var t = n(this)[0]; t.childNodes[0] && t.childNodes[0].tagName == "DIV" && t.childNodes[0].className == "GridCellDiv" && tr(t) }) } function tr(t) { for (var i = t.childNodes[0]; i.hasChildNodes() ;) t.appendChild(i.firstChild); n(i).remove() } function uu(n) { var t = r[0].scrollLeft, u = r.outerWidth(), f = n.position().left, s = n.outerWidth(); t = f + s - u + t + 5, t < 0 && (t = 0); var h = e.outerWidth() - o.outerWidth(), c = r[0].scrollWidth - r.outerWidth(), l = t / (c / h) + i.arrowsize; o.css({ left: l + "px" }), r.scrollLeft(t), a.scrollLeft(t) } function fu(n) { var u = n.position().top, f = n.outerHeight(), t = r[0].scrollTop, o = r.outerHeight(); t = u + f - o + t + 5, t < 0 && (t = 0); var h = c.outerHeight() - s.outerHeight(), l = r[0].scrollHeight - r.outerHeight(), a = t / (l / h) + i.arrowsize; s.css({ top: a + "px" }), r.scrollTop(t), i.freezesize != 0 && e[0].style.display != "none" && p.scrollTop(t) } var i = n.extend({ width: 500, height: 300, railcolor: "#F0F0F0", barcolor: "#CDCDCD", barhovercolor: "#606060", bgcolor: "#F0F0F0", freezesize: 0, arrowsize: 0, varrowtopimg: "", varrowbottomimg: "", harrowleftimg: "", harrowrightimg: "", headerrowcount: 1, railsize: 15, barsize: 15, wheelstep: 20, minscrollbarsize: 10, startVertical: 0, startHorizontal: 0, onScrollVertical: null, onScrollHorizontal: null, enabled: !0, scrollAssociate: null, verticalbar: "auto", horizontalbar: "auto" }, t), h = null, ir = !0, l = null, ct = null, st = null, vt = !1, li = !1, it = "<div><\/div>", gt = "<img />", rt = null, f = null, a = null, r = null, c = null, s = null, w = null, b = null, e = null, o = null, g = null, nt = null, et = null, p = null, ut = null, yt = null, u = null, ft = null, tt = null, k = [], ai = null, pt = null, ni = null, rr = 0, ur = 0, ot = 0, vi = 0, ht = 0, lt = -1, u = n(this), v, at, ti, ii, ri, ui, fi, ei, oi, si, yi, wt, iu, ru, bt, hi, fr, kt, ci, sr; if (u[0]) { if (!i.enabled) { gr(); return } if (l = u.children().eq(0), !(l.children().length < 2)) { if (v = i.width, at = i.height, v == "100%" && (v = n(window).width()), at == "100%" && (at = n(window).height()), ti = u.attr("id") + "Wrapper", document.getElementById(ti) ? ft = n("#" + ti) : (ft = n(it), ft.attr("id", ti), ft.css({ width: v, height: at }), u.before(ft)), ii = u.attr("id") + "PanelHeader", document.getElementById(ii) ? rt = n("#" + ii) : (rt = n(it), rt.attr("id", ii), rt.appendTo(ft)), rt.css({ background: i.bgcolor }), ri = u.attr("id") + "PanelItem", document.getElementById(ri) ? f = n("#" + ri) : (f = n(it), f.attr("id", ri), f.appendTo(ft)), f.css({ background: i.bgcolor }), ui = u.attr("id") + "PanelHeaderContent", document.getElementById(ui) ? (a = n("#" + ui), a.scrollLeft(0), a.scrollTop(0)) : (a = n(it).css({ background: "#FFFFFF" }), a.attr("id", ui), a.appendTo(rt)), fi = u.attr("id") + "PanelItemContent", document.getElementById(fi) ? (r = n("#" + fi), r.scrollLeft(0), r.scrollTop(0)) : (r = n(it).css({ background: "#FFFFFF" }), r.attr("id", fi), r.appendTo(f), u.appendTo(r)), ct = l.children().eq(0), ct.attr("id", u.attr("id") + "Header"), st = l.children().eq(i.headerrowcount), ei = u.attr("id") + "Copy", document.getElementById(ei) ? ut = n("#" + ei) : (ut = n(u[0].cloneNode(!1)), ut.attr("id", ei), ut.appendTo(a), ir = !1), dr(), oi = ct.attr("id") + "Copy", document.getElementById(oi)) yt = n("#" + oi); else if (yt = ct.clone(!1), yt.attr("id", oi), dt(yt, "Copy"), yt.appendTo(ut), i.headerrowcount > 1) for (si = 1; si < i.headerrowcount; si++) yi = l.children().eq(si).clone(!1), dt(yi, "Copy"), yi.appendTo(ut); if (r[0].style.display = "none", vi = rt[0].offsetHeight, ht = at - vi, r[0].style.display = "", wt = u.attr("id") + "PagerBottom", document.getElementById(wt)) tt = n("#" + wt), tt[0] && tt.width(v); else { var pi = l.children().eq(l.children().length - 1), tu = pi.children().eq(0), wi = tu.children().eq(0); wi[0] != null && wi[0].tagName == "TABLE" && (document.getElementById(wt) || (tt = n(it), tt.attr("id", wt), tt.addClass(pi[0].className), f.after(tt), wi.appendTo(tt), tt.width(v)), pi.remove()) } if (tt && tt[0] && (ht -= tt.height()), f.css({ position: "relative", overflow: "hidden", width: v, height: ht }), r.css({ overflow: "hidden", width: v - i.railsize, height: ht - i.railsize, zIndex: rr }), rt.css({ position: "relative", overflow: "hidden", width: v }), a.css({ overflow: "hidden", width: v - i.railsize, zIndex: rr }), br(), i.freezesize != 0 && (iu = l.children().length - 1, ru = i.headerrowcount + 1, yr()), pr(), wr(), bt = a.attr("id") + "Freeze", hi = r.attr("id") + "Freeze", i.freezesize != 0 && e[0].style.display != "none" ? ar() : (bt = a.attr("id") + "Freeze", hi = r.attr("id") + "Freeze", document.getElementById(bt) && (n("#" + bt).hide(), n("#" + hi).hide())), i.startVertical > 0 ? (kt = parseInt(i.startVertical) + i.arrowsize, y(kt, !1, !1, !0)) : i.startVertical == -1 && (fr = r.outerHeight() - s.outerHeight() - i.arrowsize, y(fr, !1, !1, !0)), i.startHorizontal > 0 && (kt = parseInt(i.startHorizontal) + i.arrowsize, d(kt, !1, !0)), i.scrollAssociate) { var bi = i.scrollAssociate.mode, ki = i.scrollAssociate.target, di = n("#" + ki + "PanelItemContent"), er = n("#" + ki + "VerticalBar"), or = n("#" + ki + "HorizontalBar"); bi == "both" ? di.bind("scroll", function () { var n = parseInt(er.css("top")), t = parseInt(or.css("left")); y(n, !1, !1, !0), d(t, !1, !0) }) : bi == "vertical" ? di.bind("scroll", function () { var n = parseInt(er.css("top")); y(n, !1, !1, !0) }) : bi == "horizontal" && di.bind("scroll", function () { var n = parseInt(or.css("left")); d(n, !1, !0) }) } if (r.bind("keyup", function (t) { var i, u, r; if (t.keyCode == 9) { if (i = n(t.target), !i[0]) return; uu(i), fu(i), u = i[0].id + "_freezeitem", r = n("#" + u), r[0] && r.focus() } }), i.freezesize != 0 && e[0].style.display != "none" && p.bind("keydown", function (t) { var i, u, r; if (t.keyCode == 9) { if (i = n(t.target), !i[0]) return; u = i[0].id.replace("_freezeitem", ""), r = n("#" + u), r[0] && r.focus() } }), !ir) return r.hover(function () { vt = !0 }, function () { vt = !1 }), i.freezesize != 0 && e[0].style.display != "none" && p.hover(function () { vt = !0 }, function () { vt = !1 }), ci = function (n) { if (vt && !c.is(":hidden")) { var n = n || window.event, t = 0; n.wheelDelta && (t = -n.wheelDelta / 120), n.detail && (t = n.detail / 3), y(t, !0), n.preventDefault && !li && n.preventDefault(), li || (n.returnValue = !1) } }, sr = function () { window.addEventListener ? (this.addEventListener("DOMMouseScroll", ci, !1), this.addEventListener("mousewheel", ci, !1)) : document.attachEvent("onmousewheel", ci) }, sr(), this } } } }), jQuery.fn.extend({ gridviewScroll: jQuery.fn.gridviewScroll }) })(jQuery)

Wednesday, 6 November 2013

Build an ASP.NET Session Timeout Redirect Control


We often get forum posts here asking "How can I tell if a user's Session is timed out, and perform some action in response?" Often this involves the incorrect assumption that the Session_End event can be used for this. ASP.NET implements a rolling timeout mechanism that extinguishes the session information for a user if no request is received within the timeout period.


Session_End happens on the server automatically regardless of whether a user has requested a page, so the idea of using it to do anything other than cleanup-type operations is a mistake; it is independent of the page lifecycle and there is no active Request or Response object to access there. 

It is often important for the business logic of an ASP.NET site to know for a particular request if the user’s session information is valid (e.g., a timeout has not occurred). Without this technique it is difficult to know, when a session variable is not found, whether it was never set properly or that the user simply waited too long between requests.  Logic normally dictates that if a session is expired, any saved state needs to be recycled back to its starting state, and typically the user should also be required to re-authenticate to the site in order to enable them to start whatever process they abandoned again from the beginning. 

ASP.NET developers habitually reference Session variables without ever checking for null first to see if they are actually present,  which causes the "Object reference not set" exception. That yellow error page doesn't look very professional to the user, either. 
So, I set out to do some more research and see what solutions might be possible. One of the ideas I got was that a site-wide session-expiry mechanism might be overkill, since usually only certain pages (such as those involved in a shopping cart) are involved in the need for protection against expired sessions. That's what brought me to think of the idea of a "drop on the page" Session Timeout "Detect and Redirect" Control. You should be able to just drop it on the pages that need it, set the redirect url, and you would be "good to go". If you do not need a page-specific solution, you can just use the Base Page class approach, or if you don't want to use a base Page class, instead you could write an HttpModule to do this and register it in web.config.) 

The only credible information I found came from a source whose work I have relied on before, Robert Boedigheimer. In sum, what Robert found was that the ASP.NET HttpSessionState class's IsNewSession( ) method returns true if a new session was created for a given request.  If this is a new session but the ASP.NET_SessionId cookie is present, this indicates a timeout situation. You may need to think about this for a while, but eventually the logic should make sense. In addition, he determined that one must access this cookie from the Request Headers collection rather than the expected Cookie collection. This is because the intrinsic Response.Cookies and Request.Cookies objects actually share the same collection, and in this test we only want to inspect the actual cookie from the Request Headers. 

With this information in hand, it was very easy to create a "non-visible" ASP.NET Server control that would hook and override a late Page LifeCycle event, PreRender, to perform this check. Add a public property for the RedirectUrl, call SignOut on any Forms Authentication to force the user to login to the site over again, and send them to the login page. Let's take a look at the code for the control:

namespace PAB.WebControls
{
    
using System;
    
using System.ComponentModel;
    
using System.Web;
    
using System.Web.Security;
    
using System.Web.UI;

    [DefaultProperty(
"Text"),
        ToolboxData(
"<{0}:SessionTimeoutControl runat=server></{0}:SessionTimeoutControl>")]
    
public class SessionTimeoutControl : Control
    {
         
private string _redirectUrl;

        [Bindable(
true),
            Category(
"Appearance"),
            DefaultValue(
"")]
        
public string RedirectUrl
        {
            get { 
return _redirectUrl; }

            set { _redirectUrl = value; }
        }


        
public override bool Visible
        {
            get { 
return true; }


        }


        
public override bool EnableViewState
        {
            get { 
return false; }
        }


        
protected override void Render(HtmlTextWriter writer)
        {
            
if (HttpContext.Current == null)
                 writer.
Write("[ *** SessionTimeout: " + this.ID + " *** ]");
            
base.Render(writer);
        }


        
protected override void OnPreRender(EventArgs e)
        {
            
base.OnPreRender(e);
             
if (this._redirectUrl == null)
                throw 
new InvalidOperationException("RedirectUrl Property Not Set.");
            
if (Context.Session != null)
            {
                 
if (Context.Session.IsNewSession)
                {
                    string sCookieHeader = Page.Request.Headers[
"Cookie"];
                     
if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                     {
                          
if (Page.Request.IsAuthenticated)
                          {
                              FormsAuthentication.SignOut();
                         }
                          Page.Response.Redirect(
this._redirectUrl);
                    }
                }
            }
        }
    }
}

n the OnPreRender event, we let the page control tree do it's thing first, then we do ours. First I check to see if the developer forgot to set the RedirectUrl property on the Control, because without that we would be all dressed up with no place to go. Then we check to see if there is actually a Session, and if so, we check the IsNewSession property. If it is true, we need to check our cookie, so we strip it out of the Request's Headers Collection and test for the "ASP.NET_SessionId" standard cookie name. If the sCookieHeader string is not null and the "ASP.NET_SessionId" cookie name is there, we know we have a timeout situation for this user. First we check to see if Authentication is being used and call the Forms SignOut method. This will ensure that the user must re-authenticate. Finally, we redirect the user to the specified redirect page on the site.

The beauty of this arrangement is that it requires no base page class to inherit from; nor does it have to be called for every Request. You simply drop the control onto any page that needs to be involved in this process. Not only that, but because there is a separate instance of the control on each page that needs it, you could even have more than one RedirectUrl depending on the particular business logic, each presenting different information to the user. The downloadable solution below contains the source code and project for the Control, as well as a test web project with a starting page (that has the control) and a redirect page. The Session Timeout in the web.config is set to 1 minute so you can easily test it.   Just wait at least one minute after requesting the TestPage.aspx and then refresh it. You'll be redirected since the Session has timed out.

NOTE: Based on a recent user post, I think it is important to understand that we are talking about Session State here, not the Timeout property of a Membership FormsAuthentication ticket. They are two completely different and separate things.  Alsom a reader came up with a potential situation where somebody might return to the site after visiting another site and still have their session cookie, thereby possibly triggering the redirect. One way to cover this would be to add the following line of code in the control just before the redirect line:

    Page.Request.Cookies["ASP.NET_SessionId"].Expires = DateTime.Now.AddDays(-100);



Method 2:

   <script language="javascript" type="text/javascript">
        var isClose = false;
        //this code will handle the F5 or Ctrl+F5 key
        //need to handle more cases like ctrl+R whose codes are not listed here
        document.onkeydown = checkKeycode
        function checkKeycode(e) {
                    var keycode;
            if (window.event)
                keycode = window.event.keyCode;
            else if (e)
                keycode = e.which;
            if (keycode == 116) {
                isClose = true;
            }
        }
        function somefunction() {
            isClose = true;
        }
        function doUnload() {
            if (!isClose) {
                var batch=document.getElementById('hdnBatch').value;
                var tan=document.getElementById('hdnTan').value;
                var role=document.getElementById('hdnRole').value;
                var user=document.getElementById('hdnUser').value;
                var rework=document.getElementById('hdnRework').value;            
                                PageMethods.bind_time(batch,tan,role,user,rework,CallSuccess, CallFailed);
               
            }
        }
    </script>
 
 
OnPageload -----------  onunload="doUnload()"
 
[System.Web.Services.WebMethod]
    public static string bind_time(string batch, string tan, string role, string user, string rework)
    {       
        Abstract_peer obj1 = new Abstract_peer();       
        obj1.closebrowser(batch, tan, role, user, rework);
        return "ss";
    }
 
protected void closebrowser(string batch, string tan, string role, string user, string rework)
    {
        DataContext db = new DataContext(strConnectionString);
        var objKey = (from t in db.GetTable<KEY_Timer>()
                      where t.strBatch == batch.ToString() && t.strTan == tan.ToString() && t.dtTimeOut == null && t.strReason == role.ToString() && t.iRework == Convert.ToInt32(rework.ToString()) && t.strLastModifiedby == user.ToString()
                      select t).FirstOrDefault();
      
        if (objKey != null)
        {
            if (objKey.dtTimeOut == null)
            {
                objKey.dtTimeOut = DateTime.Now;
                db.SubmitChanges();
            }
        }      
        ProConfig.update_timerBK(new KEY_Timer_BK { strBatch = batch.ToString(), strTan = tan.ToString(), strReason = role.ToString() });
     
    }