ASP.NET countdown timer custom control

ASP.NET countdown timer

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

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:

1
CountdownTimer1.TimeLength = new TimeSpan(2, 14, 18);
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.
Development Specialist, Artist and Activist
Personal Website

One thought on “ASP.NET countdown timer custom control

Leave a Reply

Your email address will not be published.

*