Monday, August 3, 2009

ASP.NET Session Timeout Control + jQuery Dialog + Countdown Timer

UPDATE: 8/7/2010 4:38PM
New .NET 4.0 version available here.

UPDATE: 9/27/2009 10:16PM

I originally posted this control using the jCountr jquery plugin to handle the countdown timer for me. Since then it has been brought to my attention that there were issues with this approach. Not to say the problems were from the jCountr plugin itself, but most likely my implementation of it. Regardless, I have since abandoned the jCountr plugin approach and reverted to plain javascript for handling the countdown timer. This seems to work better and give me more control over the process as well. Both attached zip files have been updated accordingly.
END UPDATE

This is my second iteration of what was originally Travis Collins' ASP.NET Session Timeout Control.

Note: You may want to read through my original post about version1 of this control as I will only be covering changes here in this post.

Essentially, v1 expanded upon Travis' original design and replaced a simple timeout warning div with a jQuery UI Dialog popup.

Based upon feedback from several users (and my own continued use of the control), I now present to you v2 of this control.


The first and most notable change is a countdown timer. If enabled, when the warning popup appears - it will countdown the time remaining until session timeout so that users can clearly see exactly how long they have left. Of course the timer calculates its countdown time based on the Timeout control's settings (which actually comes from the normal Session tag in your web.config).

The countdown timer is implemented using a new property on the control called CountDownSpanID. This controls the span element used to display the countdown timer. *Note this setting is completely optional.

The next change is the ability to control whether async/partial postbacks reset session.
This is also implemented via a new property on the control - ResetSessionOnAsyncPostback.

ResetSessionOnAsyncPostback is a boolean value that defaults to TRUE. This means even if you don't set it - the control will work just as it did in version1.

The last change is a very simple property called DirtyFormSpanID. When set, the specified span will be given a value of "False" just prior to the timeout redirect. I use this option in conjunction with other special purpose code to implement dirty form warnings if a user attempts to leave a page with unsaved changes. Please understand this control does not implement any type of dirty form warning. ALL this option does is set a span value to "False" upon timeout redirect. Obviously, this setting is completely optional.

Here's an example of how to set all three of the new properties, as well as how to easily add the span for the countdown timer:

<tsc:Timeout ID="Timeout1" runat="server" title="Session Expiring" Enabled="true" TimeoutURL="~/TimeOut.aspx" ResetSessionOnAsyncPostback="true" CountDownSpanID="tscCounter" DirtyFormSpanID="dirtyForm">
<Template>
<p>
<span class="ui-icon ui-icon-alert" style="float:left; margin: 1px 10px 20px 0;"></span>
Your session is about to Expire.
</p>
<span id="tscCounter"></span>
<p>Click <b>OK</b> to continue your session.</p>
</Template>
</tsc:Timeout>
Just remember to add references to your script files (jquery, jqueryUI, bgiframe) and you should be good to go.

Oh one more thing, if you're upgrading from the first version of my control, be aware I bumped the version number so you'll need to redo the reference to the dll.

That's it.

Full Timeout Control project (source, dll's, etc):


Full working Web App solution demonstrating usage:

63 comments:

  1. brilliant - love it

    ReplyDelete
  2. Thanks so much!
    A couple quick questions.
    tsc:Timeout is in my masterpage
    my jcounter is always 0 and doesn't countdown.
    If the session expires can i just stay on the expired page so user can copy/paste stuff off of it?

    ReplyDelete
  3. The Timeout control works the same on a master page as it does on a standalone page. I've actually been thinking of including a master + content page in the example just to illustrate because there have been a couple of questions - but honestly the code-behind and markup is exactly the same.

    If your jcounter is always 0 it makes me question the timeout values being set properly (assuming your javascript files are all properly loading). The values (which also control when the popup displays and redirects) are set via the web.config with something like the following:
    < sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" timeout="2" >

    BUT, you still have to add 2 lines of code-behind to set the timeout control to this web.config value and also calculate the AboutToTimeoutMinutes property:

    Timeout1.TimeoutMinutes = HttpContext.Current.Session.Timeout;
    Timeout1.AboutToTimeoutMinutes = HttpContext.Current.Session.Timeout - 1;

    Regarding not redirecting upon timeout - that would take a simple code change in the embedded timeout.js resource.
    down in the timeout function starting on line 209:
    timeout: function(e) { ...
    just comment out this line (214):
    window.location = this._timeoutURL;

    or i guess do whatever you'd rather do instead of the redirect.. maybe an additional alert('your session has timed-out') or whatever.

    good luck-

    ReplyDelete
  4. Thank you so much! This is really brilliant. Just a question, when I set the timeout to 5 mins, then I click "OK" upon the first warning, then do not navigate the page (it stays on the same page, and I don't click any other control), after 4 mins it's going to show the warning again...however, this time the timer is 00:00 and does not count down.

    But if I navigate or refresh the page, the problem does not occur.

    ReplyDelete
  5. Anonymous-
    Thank you very much for reporting the bug you found. After spending some time debugging, I finally abandoned use of the jCountr plugin and reverted to plain javascript for handling the countdown timer. I have updated the blog post and attached zip files. Please try the new versions and let me know if you have any issues.
    Thanks-

    ReplyDelete
  6. Wonderful! Thanks. I built something myself using the jquery ui but the implementation was sloppy. Using the current session's timeout value is much better. Really appreciate your willingness to share.

    ReplyDelete
  7. Hi, thanx!! This was exactly what I was looking for!! I have a quick question, I don't want to add the whole theme to my whole application just to use it on the timeout popup, do u have a stylesheet I can use so I only add the stylesheet when I need it?

    Thank you!

    ReplyDelete
  8. MrCaballo-

    Sorry, I don't have anything packaged up to use in place of the jquery ui theme. It should be pretty trivial though to use firebug or something and examine the ui css classes on the dialog at runtime and make a stylesheet with just those required classes. (Of course you'll also need the sprites image file too). But I would imagine for just the dialog it wouldn't end up being very much.

    Good luck-

    ReplyDelete
  9. Hey Kenneth... Sorry to keep bothering ;). Where can I change the Z-Index of the dialog??

    Thanx!

    ReplyDelete
  10. MrCaballo-

    Sorry, I don't know off-hand, but it's a regular jQuery UI dialog - so you should be able to use the UI doc for reference:
    http://www.jqueryui.com/demos/dialog/

    ReplyDelete
  11. Kenneth,
    Succesfully dropped this into a system I'm working on and have learned a bunch in the process. I ended up adding a parameter that lets me pass in another javascript function so I can capture the fields and save the entry as a draft on particular pages... very helpful for my users who may start an entry then work on something and would previously loose their already entered data when the session timed out.

    The only issue I'm having is that if a user is on a different tab they are pulled back to the system's tab when the modal pops. Doesn't seem to happen in FF but Safari (PC and Mac) and IE. Any thoughts on a way around this? I'd like for it to just timeout behind the scenes instead of taking them from whatever it is they were working on. Thanks again.

    ReplyDelete
  12. Charlie-
    Not sure off-hand, but I have a couple of questions:

    Where is the timeout control on the page relative to the tab container? (Or are the tabs on a content page and the timeout control on a master page?)

    Also, I am assuming you are referring to the jQuery UI tabs?

    Is the system tab you mention the first/default tab shown when the page first renders?

    Thanks-

    ReplyDelete
  13. its actually not a ui tab... Its a browser tab. Should have been clear on that. The user has opened a new tab and gone to google something. I put the modal and all the components in a control and they are on the MasterPage.

    ReplyDelete
  14. Charlie-
    Ah, I didn't even think about browser tabs!

    Unfortunately I don't think I have an answer for you on that one. That sounds like it may be just how the browser tabs behave? I will try to do a little research though and see.

    If you find out anything too - let me know-

    Thanks-

    ReplyDelete
  15. Can this be used in a MasterPage?

    When I reference the timeout.js file I get this error.

    Error: Type is not defined timout.js

    Also when the application times out and then is supposed to redirect the user i get this:

    $get(this._dirtyFormSpanID) is null.

    Anyone else have this problem?

    Thanks for the help. The new release is looking great!

    ReplyDelete
  16. Mike-
    Sure, it can easily be used on a MasterPage.

    BTW, you don't reference the timeout.js file at all. It's embedded in the compiled dll.

    I think maybe you downloaded the timeout control's source project - you should download the other zip attached to the blog post. It is a full working web app solution. It doesn't have a master/content page example, but it shows how to use it on a single page setup. Just apply that to the master page. Let me know if you have problems - i've been meaning to go back and add a master/content page to that sample app anyway.

    Good luck-

    ReplyDelete
  17. Kenneth,
    FYI - I ended up putting a span on the page that was populated with "hasfocus" or "lostfocus" using
    $(window).bind("blur",$(#spanId).text("lostfocus");

    and

    $(window).bind("focus",$(#spanId).text("hasfocus"));

    Then dropped a bit in your build that checks that span's value. If it says "lostfocus" it just runs by logout script and then drops to the logouturl.

    Thanks again for the code and the great learning experience.

    ReplyDelete
  18. Has anyone experienced the issue while using this control where sometimes after the page loads you will get the unable to modify parent container ie error in the lower left hand corner? I am using this control in a master page. Once I remove the control the error goes away. I get it to reproduce after refreshing the page say 10 or so times.

    ReplyDelete
  19. Anonymous-
    I've never seen that before - can you reproduce it using the posted demo app? Also, what browser & version just out of curiosity?

    Thanks-
    Kenneth

    ReplyDelete
  20. I haven't tried the posted demo app. I have received the same error with ie8 and ie8 in ie7 compatibility view. I'm wondering if some of the script is trying to modify the dom before the document is ready. Right now I'm looking at the javascript to see if we need to add a $document.ready() block somewhere

    ReplyDelete
  21. Ken, I need to have the control NOT open the "TimeoutUrl" automatically when the counter goes to 0 or negative. (comment line 214 timeout.js)
    I only want to go there when OK is clicked and the countdown is 0 or negative. Can i tell when the countdown is 0 or less?

    ReplyDelete
  22. Anyone having an issue where the redirect does not fire? I am using the control without the timer and it works fine. Changing to use the timer control failes to redirect

    ReplyDelete
  23. Nevermind my other post about not redirecting to upon timeout forgot to put DirtyForm span in the page

    ReplyDelete
  24. Anonymous-
    That DirtyForm span should have been completely optional. I'll double-check and make sure it doesn't cause problems if omitted.

    Thanks-
    Kenneth

    ReplyDelete
  25. Anonymous-
    If you remove the DirtyFormSpanID property from the control, you shouldn't need the span on the page. Was the property set maybe?

    ReplyDelete
  26. Anonymous (12/11/2009 6:09 PM)-
    Sorry but I don't see any way to accomplish what you're after without making some code changes.

    You can override the code performed when the OK button is clicked (without actually changing the control) - but to know when the timeout timer expires, I think you're going to need to change the "timeout" function in the code.

    Good luck-

    ReplyDelete
  27. Kenneth,
    I am currently working on a situation where I can gracefully handle the Session Timeout (and Forms Authentication), but I have an additional requirement - to enable the session to be refreshed, behind the scenes. I was wondering what your thoughts are on adding a few additional variables to the control maxSessionExtensions and sessionRefreshURL, where the session will refreshed n-many times in the background (keeping session variables in tact) until the limit is reached and then showing the popup and timing out/logging out the user. I have accomplished this pretty much, in javascript, but I am a newbie when it comes to jQuery. Do think this is possible with this control? Any thoughts or suggestions on implementing it?

    Thanks,

    Scott

    ReplyDelete
  28. I was wondering if anyone had tried to add in a session refresh function (with a max refresh variable of course), that would allow for the session to be refreshed in the background, extending timeout? I am working on it with this control, but I am new to ASP.NET.

    Thanks,

    Scott

    ReplyDelete
  29. Scott-
    Sorry for just now responding. I think you could probably accomplish what you're wanting without too much trouble. There's probably several ways you could go about it, but the simplest might be to just modify showAboutToTimeout() so that you check a private var counter against your new maxSessionExtensions parameter and if LT/EQ call CallServer() and _resetTimeout().. else just let it do the popup..

    'course that's all untested and completely theoretical, but i think it'd work :)

    ReplyDelete
  30. Is there any way to use this without using the script manager control? My work doesnt allow it...

    ReplyDelete
  31. Anonymous-
    Sorry, no it's dependent on a script manager at this point.

    thanks-

    ReplyDelete
  32. Hi,

    This control has been a great help but i am facing one problem with this. I have added this on to header control which is rendered in all the pages.

    Now when i am trying to render from the default page location then it works fine but when i am trying to render in pages which are in ADMIN folder it gives jQuery.UI as undefined and hence it is not showing model dialog. So can you help me on this? This problem has taken very long time.

    Thanks

    ReplyDelete
  33. Anonymous-
    Any page that could possibly use the timeout control must also have the jquery framework and jquery ui loaded.
    I suggest you simply add the jquery and jqueryui js file loads to the header control you mentioned.

    Good luck-

    ReplyDelete
  34. Hi All,

    i am using jquery.ui.core .js file also for Date range picker when i comment this js file only the session time out popup i can able to see other wise i can't and when i comment the baove js file i can't able to do the date range picker .so please if any one have the solution pass it

    ReplyDelete
  35. Hi Kenneth,

    I have my Timeout control in my Masterpage. I initialize it when a user logs in to the web app. However I have noticed that the counter is approximately 5 seconds out of sync with my session expiration. In other words, if I hit "OK" when there are 5 or less seconds remaining in the counter, my Session would have already expired even though I hit the button. Do you have any idea of why this is happening? Does the ResetSessionOnAsyncPostback="true" property has something to do with this issue?? What does an AsyncPostback really means in a practical sense?

    Thank you,
    MrCaballo

    ReplyDelete
  36. MrCaballo-
    I suppose what you are describing would be possible with a large page of content to render and/or a slow connection.

    You have to remember there's no truly perfect way of doing this because of the disconnected nature of the web. All we're trying to do is start a clientside timer that can hopefully stay in sync with the server... But if it takes the client awhile to receive the page data (plus render it) - it could delay the creation (and start) of the clientside timer causing a discrepancy like you mention.

    I doubt it has anything to do with the async setting. That just controls whether the timer is reset upon an asynchronous postback or just a full normal synchronous postback. Async postbacks are those called via clientside code.

    You could always adjust the timer seconds to compensate but I don't know if that'll guarantee it to work perfectly.

    You might try playing around with the placement of the control on the page too.

    ReplyDelete
  37. How do I adjust the timer seconds??

    ReplyDelete
  38. One Suggestion:

    After the user click on "Ok" button give the user a kinda confirmation message that your session has been extended to {time}

    what do you think?, and how will i implement?

    Thanks.

    ReplyDelete
  39. Does the minutes remaining automatically refresh? If not, is there a way to enable this?

    ReplyDelete
  40. DogEars-

    'not really sure what you mean exactly. If the user clicks the button on the popup, the timer will reset and extend their session.
    Of that's not the behavior you're seeing, try the demo and see if it works for you.

    Good luck-
    Kenneth

    ReplyDelete
  41. Kenneth - This is really useful! Thanks a lot!

    ReplyDelete
  42. I implemented this control in 1-2010
    and think that it may need to have the
    ViewStateModeById attribute set as I
    got one failure in production with View state loading by Index problem.

    [ViewStateModeById]
    public class Timeout : WebControl, INamingContainer, IScriptControl, ICallbackEventHandler
    {....

    ReplyDelete
  43. hi, i am using a master page, pls cau give steps how to implement it in my project, in step by manner.. i m new in asp.net development field. But i hav this requirement and like your post very much..downloaded the file "TSC.Timeout_With_Counter". now how to implement it.

    ReplyDelete
  44. Wow.... This control works very well and i am really happy that you shared your hard work with all of us.

    Have a great day and thank you.

    ReplyDelete
  45. Manu-

    Thanks! Be sure to check out the updated .net 4.0 version!

    Kenneth

    ReplyDelete
  46. Hi Kenneth,
    Best Stuff for ASP.Net.
    My query is, I wish to show the time of session time on my web page as,
    "Time Remaining for Session Time out: {Time of count down timer.}"
    Also, this timer should also get reset on clicking "OK" button.

    Thanx!!

    Do reply!!!

    ReplyDelete
  47. Hey Kenneth,

    Thanks for such a wonderful stuff.
    I need your help to sort out one problem in this stuff.
    In the pop up dialog box, there is one countdown timer, I need to show it on the page load itself and reset it when click on OK.
    If this is not possible, how can I trap the click event of Ok button, so that I can Add one JavaScript timer on my top of the page.

    My Web application don't have the master page, its only simple aspx pages.

    You can also get in touch with me at
    sandeep.parandekar@gmail.com

    Regards
    Sandeep Parandekar
    (Sandy)

    ReplyDelete
  48. Hey Kenneth,

    How can I trap ok Button click from the pop up window, I have to call a javascript function on that ok button click.
    please help me.

    Regards
    Satyajit

    ReplyDelete
  49. Sandeep and Satyajit-
    If you just want to execute some custom javascript when the OK button is clicked, you can always just override it like I showed at the end of the original (version1) post. Then you can put whatever code you want in there.

    Sandeep-
    Regarding you question about an additional timer - the easiest way to do that might be to just have your own timer on the page (completely independent of the timeout control). You can easily set it up using your Session Timeout value (like we get programmatically in the code-behind for the timeout control where we set timeout and aboutToTimeout amounts). Then just override the OK button (like I mentioned earlier) and reset your new timer in there.

    good luck-

    ReplyDelete
  50. Hello Kenneth,

    Thanks for your helpful suggestion, It really solved my problem.

    I need one more help from you, as I need to add one more button to the pop up box as "Log Off" and after clicking on it, I have to redirect to my Home page.
    can you please help me out, I have tried to implement same, for Ok Button, But not fruitful for me.


    Regards
    Sandeep

    ReplyDelete
  51. Hi Kenneth,

    Such a best script!

    But, I have query,

    I wish to add a button next to 'OK' button, so that, on clicking it, user is directly navigated to log-in page, such as 'Log-off'.

    I tried this, but it won't worked:

    initDialog: function(e) {
    if (typeof jQuery.ui != 'undefined') {
    var tsc = this;
    $("#" + this._clientId).dialog({
    autoOpen: false,
    bgiframe: true,
    modal: true,
    buttons: {
    Ok: function() {
    $(this).dialog('close');
    CallServer();
    tsc._resetTimeout();
    }
    Log-out: function(){
    $(this).dialog('close');
    CallServer();
    tsc._timeoutURL();
    }
    }
    });
    }
    },

    Also, I wish to ask one thing that, I have 'frame' on my one of the page on which two other pages are joined. I wish to apply this script on that page, so that it is applicable to both the pages on that page.

    Thankx!!!!


    Do reply!!

    It's urgent.

    ReplyDelete
  52. I implemented your code and it will timeout out and redirect after 2 minutes, just no dialog shows up at all. Any suggestions?

    ReplyDelete
  53. Tom-
    Are you sure that jQuery and the jQuery UI are loaded on your page? Also make sure there aren't any javascript errors visible.

    Kenneth

    ReplyDelete
  54. Anonymous-
    If you want to add a button, you'll have to add markup of some form in the template (i.e. an input element or server-side button, etc).

    I'm honestly not sure about usage in a frames situation such as yours. I try to stay very far away from frames and as a result have very little experience with them. Please post your findings for the benefit of others if you get it to work though- sorry i couldn't be more help-

    Good luck-

    ReplyDelete
  55. i am adding the timeout popup to my existing asp.net ajax driven site(vb). I already had a script control on page but keep getting the following error on startup:
    [HttpException (0x80004005): A ScriptManager control must exist on the page.]
    TSC.Timeout.Timeout.OnPreRender(EventArgs e) +177
    System.Web.UI.Control.PreRenderRecursiveInternal() +80
    System.Web.UI.Control.PreRenderRecursiveInternal() +171
    System.Web.UI.Control.PreRenderRecursiveInternal() +171
    System.Web.UI.Control.PreRenderRecursiveInternal() +171
    System.Web.UI.Control.PreRenderRecursiveInternal() +171
    System.Web.UI.Control.PreRenderRecursiveInternal() +171
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +842

    I have moved the scriptmanager reference to several different places in code but no success.

    Do you know why this error keeps page from running or how I can overcome this issue.

    Thanks

    Mike

    ReplyDelete
  56. Mike-
    I'm wondering if it's a difference in versions of System.Web.Extensions.dll.

    I believe this version of the control is dependent on 1.0.61231 which I think is a .NET 2.0-era version.

    Since you mentioned your site is already ajax .net enabled - I'd probably just grab the source for the timeout control, remove the old dll reference (which is included in the "libs" folder within the source, btw) - and just reference whatever version your site is using and recompile.

    let me know if that works-

    good luck-

    ReplyDelete
  57. You ave really nice Blog here and I am saving it in my favorite list. Keep posting similar stuff.

    ReplyDelete
  58. If I resize the browser window while the timeout warning is up, the browser crashes. No idea why... jQuery bug?

    ReplyDelete
  59. Hi this is cool. The sample with master page (Dot.net 4) runs fine but when I implemented it on my application here is what is happening.

    http://imageshack.us/photo/my-images/594/unled2tr.jpg/


    http://imageshack.us/photo/my-images/200/unledjcd.jpg/

    ReplyDelete
  60. Siteslayer-

    Are you sure you have a jquery ui theme setup and functioning properly?

    ReplyDelete
  61. Sorry Kenneth, I am so stupid I had kept the the css reference in content placeholder of master page, after correcting that it works like a charm.
    Thanks for this amazing control. Looking forward for more stuff from you.

    Thanks & Regards
    Ravi Roy

    ReplyDelete
  62. Thanks for the great control, would appreciate if you can advise if It’s possible to highlight (set focus) on the page once the alert appear, so that user are aware that the session is going too expired. Currently if I open a few browsers in the same time I will not aware on the notification. It would be very great if this can be implemented as it will be more interactive.
    Thanks.

    Best Regards,
    Darren

    ReplyDelete