Soham Banerjee
Soham Banerjee

Reputation: 465

System.Timers.Timer not working edit [in asp.net web forms]

I am trying out the Timer class with this code:-

protected void Page_Load(object sender, EventArgs e)
{
    System.Timers.Timer tm = new System.Timers.Timer();
    tm.Elapsed += new System.Timers.ElapsedEventHandler(tm_Elapsed);
    tm.Interval = 1000;
    tm.Start();
}

void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    int lbl = Convert.ToInt32(Label1.Text);
    Label1.Text = (lbl+1).ToString();
}

Initially, the Label1.Text is "1".

But when I run the application, the label's text shows 1 and doesn't increase.

Upvotes: 4

Views: 3980

Answers (5)

Adil
Adil

Reputation: 148120

As its already mentioned in the other answers the System.Timers.Timer is fired on non-GUI thread. This wont allow you access the GUI element and would raise cross thread exception. You can use MethodInvoker to access the GUI element in tm_Elapsed event. Since you have the Timer in Forms and want to access GUI element the other Timer class suits you most i.e System.Windows.Forms.Timer.

Implements a timer that raises an event at user-defined intervals. This timer is optimized for use in Windows Forms applications and must be used in a window.

protected void Page_Load(object sender, EventArgs e)
{         
     System.Windows.Forms.Timer tm = new System.Windows.Forms.Timer();
     tm.Tick += tm_Tick;
     tm.Interval = 1000;
     tm.Start();
}

void tm_Tick(object sender, EventArgs e)
{
     int lbl = Convert.ToInt32(label1.Text);
     label1.Text = (lbl + 1).ToString();
}

Edit based on comments by OP, that he is doing this in web page not win forms as the load event name suggests.

You can use javascript if you do not need anything from server. If you want update the html control and need to do it from server then you can use asp:Timer

Html (.aspx)

  <form id="form1" runat="server">             
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <asp:Timer runat="server" id="UpdateTimer" interval="5000" ontick="UpdateTimer_Tick" />
        <asp:UpdatePanel runat="server" id="TimedPanel" updatemode="Conditional">
            <Triggers>
                <asp:AsyncPostBackTrigger controlid="UpdateTimer" eventname="Tick" />
            </Triggers>
            <ContentTemplate>
                 <asp:Label id="Label1" runat="server" Text="1" />
            </ContentTemplate>
        </asp:UpdatePanel>
    </form>

Code behind

protected void UpdateTimer_Tick(object sender, EventArgs e)
{
    Label1.Text = int.Parse(Label1.Text) + 1;
}

Upvotes: 3

insilenzio
insilenzio

Reputation: 938

Try using System.Threading.Timer (Thread safe) combined with @Enigmativity solution to avoid Cross-thread exception:

    private System.Threading.Timer m_timer = null;
    private const int DUE_TIME = 1000;
    private const int INTERVAL = 1000;

    private void Page_Load(object sender, EventArgs e)
    {
        System.Threading.AutoResetEvent autoEvent = new System.Threading.AutoResetEvent(false);
        System.Threading.TimerCallback tcb = new System.Threading.TimerCallback(timer_Elapsed);
        m_timer = new System.Threading.Timer(tcb, null, DUE_TIME, INTERVAL);
    }

    void timer_Elapsed(object sender)
    {
        label1.Invoke((Action)(() =>
        {
            int lbl = Convert.ToInt32(label1.Text);
            label1.Text = (lbl + 1).ToString();
        }));
    }

Upvotes: 0

Abhishek
Abhishek

Reputation: 7045

You can try following,

if(this.Label1.InvokeRequired)
     {
         this.Label1.BeginInVoke((MethodInvoker) delegate() { int lbl = Convert.ToInt32(Label1.Text);
Label1.Text = (lbl+1).ToString();});    
     }
     else
     {
         int lbl = Convert.ToInt32(Label1.Text);
         Label1.Text = (lbl+1).ToString();
     }

The problem is, WPF/Windows Forms/Windows 8 app/WP 8(.1) apps doesn't allow UI to changed from different threads. You need UI thread to update it.

Upvotes: 0

M.Mahdipour
M.Mahdipour

Reputation: 603

System.Timers.Timer is intended to be used in a multithreaded environment. You are not allowed to directly access ui elements inside the Elapsed event.

If your application is Windows Forms, use System.Windows.Forms.Timer. If your application is WPF, use System.Windows.Threading.DispatcherTimer.

If for any reason you want to use System.Timers.Timer, use Invoke method of your ui elements. For example in Windows Forms:

Label1.Invoke(new Action(() =>
{
    int lbl = Convert.ToInt32(Label1.Text);
    Label1.Text = (lbl+1).ToString();
}));

Upvotes: 1

Enigmativity
Enigmativity

Reputation: 117064

Try this:

    void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Label1.Invoke((Action)(() =>
        {
            int lbl = Convert.ToInt32(Label1.Text);
            Label1.Text = (lbl+1).ToString();
        }));
    }

The issue is that System.Timers.Timer fires its event in an non-UI thread and since you can't access or update controls safely from a non-UI thread it appears not to work.

Calling .Invoke(...) on a UI element allows you to push code onto the UI-thread making it safe.

Upvotes: 1

Related Questions