Wednesday, April 9, 2008

Client Side Timer Component, ASP.NET AJAX

You can download source code for control only, sample with control, and see the sample online.

We can use server side timer control that is available in AJAX library. But often we need timer that works on client only. Server Timer control posts back every time when tick event is raised. I need the similar control, that raises this event on client.

I found one very good sample, and to not waste time and reinvent already existing, I used this sample to create reusable Timer control.

The sample is from online AJAX docs website, Custom Demo.Timer Component

Here is a code of sample page demonstrating how to use the control:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="Devarchive.Net" Namespace="Devarchive.Net" TagPrefix="dn" %>
<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <style type="text/css">
        #divOutput
        {
            border: dashed 1px black;
        }
        .highlight
        {
            background-color : Silver;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <dn:Timer runat="server" ID="Timer1" Interval="1000" ComponentID="t1" OnTick="onTick" />
        <script type="text/javascript">
            var i = 0;
            var b = false;
            function onTick() {
                $get("divOutput").innerHTML = new Date().format("HH:mm:ss tt");
                $get("divOutput").className = b ? "highlight" : "";
                b = !b;
            }
            function enableTimer() {
                $find("t1").set_enabled(true);
                updateStatus();
            }
            function disableTimer() {
                $find("t1").set_enabled(false);
                updateStatus();
            }
            function pageLoad(sender, args) {
                $find("slider").add_valueChanged(sliderChanged);
                updateStatus();
            }
            function sliderChanged(sender, args) {
                var interval = parseInt($find("slider").get_Value());
                $find("t1").set_interval(interval);
                updateStatus();
                
            }
            function updateStatus() {
                var interval = $find("t1").get_interval();
                var enabled = $find("t1").get_enabled();
                $get("divDetails").innerHTML = 
                    String.format(
                        "Interval: {0} ms<br />Enabled: {1}", 
                        interval,
                        enabled
                        );
                $get("btnDisabled").disabled = !enabled;
                $get("btnEnabled").disabled = enabled;
            }
        </script>
        <div id="divOutput">&nbsp;</div>
        <div id="divDetails">&nbsp;</div>
        <asp:TextBox runat="server" ID="tbInterval" Text="1000" />
        <cc1:SliderExtender BehaviorID="slider" ID="slider" Minimum="10" Maximum="10000" 
            TargetControlID="tbInterval" runat="server">
        </cc1:SliderExtender>
        <br />
        <input type="button" id="btnEnabled" value="Enable Timer" onclick="enableTimer();" />
        <input type="button" id="btnDisabled" value="Disable Timer" onclick="disableTimer();" />
    </form>
</body>
</html>

 

As you see, markup for the control on the page is:

<dn:Timer runat="server" ID="Timer1" Interval="1000" ComponentID="t1" OnTick="onTick" />

Interval - interval in milliseconds,

Enabled - set true to activate timer, or false to deactivate it,

ComponentID - assign some id to the control, using this id the component can be referenced from the script using syntax: $find("<ComponentID>), if not specified, ClientID is used

OnTick - specify client side function which will be called on every tick event. Alternatively you can attach event handlers from javascript using syntax:

$find(<ComponentID>).add_tick(myHandler), and detach them using syntax: $find(<ComponentID>).remove_tick(myHandler).

 

The running sample page looks like this:

image

 

Hope this helps.

Technorati Tags:

kick it on DotNetKicks.com

14 comments:

Anonymous said...

nice one! EXACTLY what i was looking for!
--nick

Anonymous said...

I have searched the net for exactly this solution. Thanks.

Anonymous said...

Hello.
more links for that topic?
And Bye.

Navin said...

That's great...thank you. I'm trying to use it in another user control which could have multiple instances on the page. In that case, somehow only on one instance of user control it is working and not on the other. Any ideas ? BTW the page already has scriptmanager and updatepanel on it.

Thanks in advance and regards

Kirill Chilingarashvili said...

Hi Navin

make sure you use different ComponentIDs for different components.

Also look at differences between Sys.Component and Sys.UI.Controls
component is not associated with user controls - it has no idea about markup - it is component - it works without knowledge about visual representation.
While Sys.UI.Control works with element (when creating it constructor must accept element to which the script have to be associated). So if you want to associate timer to control and make sure the timer script creates and disposes properly when associatred control is added / removed using AJAX UpdatePanel or your custom mechanism - then it is better to convert my sample from Sys.Component to Sys.UI.Control - in that case you will not have problems when adding/removing control during async postback/UpdatePanel.


Regards,
Kirill

Navin said...

Thanks for the response...appreviate it. I guess that explains the problem I'm seeing. Each instance of Timer user control(used in my own user control) was having same ID in page markup but generated ID was different when viewing page source. Still only one Timer user control was updating. I tried to create the Timer user control dynamically too and tried to assign a unique ID in page load but still the same issue :(

Is it simple enough to change the Timer user control to derive from Sys.UI.Control instead ?

Thanks again and regards

Navin

Kirill Chilingarashvili said...

Hi Navin

no it is not complex.

please also look at this: http://blog.devarchive.net/2008/04/using-iscriptcontrol-interface-with.html
article.

Regards,
Kirill

Navin said...

Thanks. In my user control, I tried to make it a ScriptControl too with timer functionality and it worked. Thanks bunches!!! Now one thing I need to do to start/stop the timer from code behind of the user control itself. I've a GridView in my user control. When new data is available, I do databind and I want to stop the timer and start it again from the new value. I set its Enabled property to false but I guess it doesn't work because it needs a postback. How I could stop/start the timer in that manner ?

Thanks again and regards

Kirill Chilingarashvili said...

Hi Navin,

You can start/stop timer from client code (js) or from c# code before rendering a page.

The client is client side component and has nothing to do with page lifecycle.

you can only enable disable from code behind - saying what enabled state will be for timer on page startup.

After this it lives on client.

Regards,
Kirill

Navin said...

Thanks for clarification. My missin piece is that I want to enable/disable timer from code behind too depending upon some condition. I understand it has to be done from client side. How could I disable the timer, set a new value to increment and then reenable the timer when a new value is set for an asp:label in code behind(and so will be available after partial postback as I've UpdatePanel too on the page) ? Do I need to load a startup script to do that ?

Or should I use hidden fields to store such information and enable/reset/disable timer accordingly in its onTick callback ?

Thanks again and regards

Navin

Kirill Chilingarashvili said...

Navin,

you can do it using startup script (ScriptManager has the method you need).

or

you can do it using hidden field.,

I still dont quite understand the requirement - the timer is client component - if you need server side timer - use Timer control from ASP.NET AJAX.

But yes you can interop with client timer in any way you wish - the same way you would with any other client side component.

Regards,
Kirill

Navin said...

Hi,

I finally got it working and now I've a timer in my user control and I use client timer user control inside it. One wierd thing I'm seeing is when using it in a page which has another timer. The main page level timer fires every 2 seconds and calls a page method to check for any updates to the page(which has UpdatePanel inside it too). The user control fires every second to increment a counter. What is happening is, when using it in combination with the page level 2 second timer, the user control level timer fires multiple times in a second! As a result my counter jumps up too fast. The main window timer has been set using window.setTimeout and the user control timer is set using window.setInterval. Can't both be used together ?

Thanks in advance and regards

Dedicated ASP .Net Developer said...

I was stuck on the same point but you have provided the explanation in such an easy way that I got the things. Thanks a lot it helped me a lot. Keep it up.

Pravesh Singh said...

Check out this helpful link too its also having nice post with wonderful explanation on
http://mindstick.com/Articles/385ebacd-89e2-40d3-868a-0185e7c45f15/?ASP.Net%20AJAX%20Server%20Control:%20Timer

Thanks