
Recently I had the need to create an ASP.NET countdown timer for one of my projects.
I needed the ASP.NET countdown timer custom control to:
- run at client side (java script)
- have the ability to use the countdown timer several times on the same page with different timers
- configure the end action for each counter
- end action for any timer can be: : just stop, refresh page, make an ajax post or partial post.
- bu used just like any other control, drag n drop to the page, set timer and it’s ready!
you should create a library for the controls, I used ‘CF.Controls’.
The project includes a test project available for download at git: https://github.com/ET-CS/ASP.net-Countdown-Timer-Webform-Control
CountdownTimer.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
namespace CF.Controls
{
public enum EndAction{
doStop = 0,
doRefresh = 1,
doPost = 2,
doPartialPost = 3}
/// <summary>
/// CF.Controls.CountdownTimer Control
/// </summary>
[ToolboxData("<{0}:CountdownTimer runat=server />")]
public class CountdownTimer : ScriptControl, INamingContainer, ICallbackEventHandler
{
#region "properties"
private TimeSpan timelengthvalue;
[Category("Behavior")]
[Description("Total TimeSpan to countdown")]
public TimeSpan TimeLength
{
get { return timelengthvalue; }
set { timelengthvalue = value; }
}
private EndAction endactionvalue = CF.Controls.EndAction.doPartialPost;
[Category("Behavior")]
[DefaultValue(EndAction.doPartialPost)]
[Description("End action to perform when time out")]
public EndAction EndAction
{
get { return endactionvalue; }
set { endactionvalue = value; }
}
private bool onlinevalue = true;
[Category("Behavior")]
[DefaultValue(true)]
[Description("Enable or Disable JS timeOut On Countdown")]
public bool Online
{
get { return onlinevalue; }
set { onlinevalue = value; }
}
private string timeoutvalue;
[Category("Behavior")]
[DefaultValue("")]
[UrlProperty]
[Description("URL to redirect the user, in the event of a membership session timeout")]
public string TimeoutURL
{
get { return timeoutvalue; }
set { timeoutvalue = value; }
}
#endregion
#region "Callback"
public void RaiseCallbackEvent(String eventArgument)
{
// All we're doing here is resetting the sliding expiration
// of the forms authentication. No additional code is needed.
}
public String GetCallbackResult()
{
// return an emtpy string. We're not really interested
// in the return value.
return String.Empty;
}
#endregion
public CountdownTimer()
{
//
// TODO: Add constructor logic here
//
}
protected override IEnumerable<ScriptDescriptor>
GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor("CF.Controls.CountdownTimer", this.ClientID);
// Add our TimeLength property to be passed down to the client
descriptor.AddProperty("timelength", this.timelengthvalue.Ticks);
descriptor.AddProperty("date", DateTime.UtcNow.Add(this.timelengthvalue));
descriptor.AddProperty("endaction", this.endactionvalue);
descriptor.AddProperty("enabled", this.Online);
yield return descriptor;
}
// Generate the script reference
protected override IEnumerable<ScriptReference>
GetScriptReferences()
{
yield return new ScriptReference("CF.Controls.CountdownTimer.js", this.GetType().Assembly.FullName);
}
/// <summary>
/// returns the number of milliseconds since Jan 1, 1970 (useful for converting C# dates to JS dates)
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
Double UnixTicks(DateTime dt) {
DateTime d1 = new DateTime(1970, 1, 1);
DateTime d2 = dt.ToUniversalTime();
TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks);
return ts.TotalMilliseconds;
}
#region "Render Control"
void InitialiseControls() {
Label lbl = new Label();
lbl.ClientIDMode = ClientIDMode.AutoID;
lbl.Text = TimeLength.Ticks.ToString();
this.Controls.Add(lbl);
}
protected override void OnPreRender(EventArgs e)
{
if (!this.DesignMode)
{
if (ScriptManager.GetCurrent(Page) == null)
{
throw new HttpException("A ScriptManager control must exist on the current page.");
}
InitialiseControls();
String cbReference =
Page.ClientScript.GetCallbackEventReference(this,
"arg", "ReceiveServerData", "context");
String callbackScript;
callbackScript = "function CallServer(arg, context)" +
"{ " + cbReference + ";}";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"CallServer", callbackScript, true);
}
base.OnPreRender(e);
}
#endregion
}
}
CountdownTimer.js
/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace("CF.Controls");
CF.Controls.CountdownTimer = function (element) {
//this._element = element; // div control that contains everything
CF.Controls.CountdownTimer.initializeBase(this, [element]);
// "private" variables to hold the properties values
this._timelength = null;
this._date = null;
// doStop = 0 , doRefresh = 1 , doPost = 2 , doPartialPost = 3
this._endaction = 3;
// 0 false, 1 true
this._enabled = 1;
// timer pointer
this._timerTimeout = null;
this._starttime = null;
}
CF.Controls.CountdownTimer.prototype = {
initialize: function () {
CF.Controls.CountdownTimer.callBaseMethod(this, 'initialize');
// Add custom initialization here
var elem = this._element.childNodes;
for (i = 0; i < elem.length; i++) {
doc = elem[i];
var type = doc.type;
}
//start timer
this._starttime = new Date().getTime();
this._resetTimeout();
this._timeoutDelegate = Function.createDelegate(this, this.timeout);
this._timerTimeout = setTimeout(this._timeoutDelegate, 0);
},
dispose: function () {
//Add custom dispose actions here
this._resetTimeout();
this._timelength = null;
CF.Controls.CountdownTimer.callBaseMethod(this, 'dispose');
},
// timelength Property Accessors
get_timelength: function () {
return this._timelength;
},
set_timelength: function (value) {
this._timelength = value;
this.raisePropertyChanged('timelength');
},
// target date Property Accessors - Depreceated
get_date: function () {
return this._date;
},
set_date: function (value) {
this._date = value;
this.raisePropertyChanged('date');
},
// Endaction Property Accessors
get_endaction: function () {
return this._endaction;
},
set_endaction: function (value) {
this._endaction = value;
this.raisePropertyChanged('endaction');
},
// Enabled Property Accessors
get_enabled: function () {
return this._enabled;
},
set_enabled: function (value) {
this._enabled = value;
this.raisePropertyChanged('enabled');
},
_resetTimeout: function (e) {
clearTimeout(this._timerTimeout);
this._timerTimeout = null;
},
timeout: function (e) {
//calculating
var currentdate = new Date();
//method 2: calc from time
var jsticks = this._timelength / 10000;
var timelength = new Date(jsticks).getTime() - 7200000;
var difftime = timelength - (currentdate.getTime() - this._starttime - 7200000) - 7200000;
//check if time is gone - and do events
if (difftime < -7200000) {
switch (this._endaction) {
case 0: break;
case 1: location.reload(true); break;
case 2: __doPostBack(); break;
case 3: __doPostBack(this._element.id); break;
default:
}
}
else {
var diff = new Date(difftime);
//alert(diff);
var d = getDays(diff.getTime());
//alert(d);
var curr_hour
if (d > 0) {
curr_hour = formatzero(d * 24 + diff.getHours());
} else { curr_hour = formatzero(diff.getHours()); }
var curr_min = formatzero(diff.getMinutes());
var curr_sec = formatzero(diff.getSeconds());
//if (curr_hour == 23) { alert(diff.getTime()) };
var str = curr_hour + ":" + curr_min + ":" + curr_sec;
//showing
span = document.getElementById(this._element.id)
if (span != null) {
var elem = span.childNodes;
elem[0].innerHTML = str;
//timering
if (this._enabled == 1) {
this._timeoutDelegate = Function.createDelegate(this, this.timeout);
this._timerTimeout = setTimeout(this._timeoutDelegate, 350);
//setTimeout("displayCountdown('" + obj + "','" + desttime + "','" + time + "','" + start + "'," + endaction + "," + online + ")", 900);
}
};
}
}
}
function getDays(d)
{
var ONE_DAY = 1000 * 60 * 60 * 24;
return Math.round(d/ONE_DAY);
}
function formatzero(obj)
{
return ((obj) > 9) ? (obj) : '0' + (obj);
}
function ReceiveServerData(rValue) {}
CF.Controls.CountdownTimer.registerClass('CF.Controls.CountdownTimer', Sys.UI.Control);
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
The project includes a test project available for download at git: [https://github.com/ET-CS/ASP.net-Countdown-Timer-Webform-Control](https://github.com/ET-CS/ASP.net-Countdown-Timer-Webform-Control "https://github.com/ET-CS/ASP.net-Countdown-Timer-Webform-Control")
## Using CountdownTimer
Using the custom control inside .aspx page to use this custom control, A ScriptManager control must exist on the page. in your .aspx page you need to declare it:
<%@ Register Assembly="CF.Controls" Namespace="CF.Controls" TagPrefix="cc1" %> <cc1:CountdownTimer ID="CountdownTimer1" runat="server" EndAction="doRefresh" />
and add a counter using: set timer from code-behind is easy too:
```csharp
CountdownTimer1.TimeLength = new TimeSpan(2, 14, 18);
you can set the EndAction (CountdownTimer1.EndAction=) to either doStop, doRefresh, doPost or doPartialPost (Default).
- doStop - Stop at the end
- doRefresh - Refresh at the end
- doPost - Do full post
- doPartialPost (Default) - Do partial post (using the microsoft ajax framwork)
Set the .online parameter to ‘disabled’ to disable countdown.
A ScriptManager control must exist on the current page.
