4

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.

Adil
  • 146,340
  • 25
  • 209
  • 204
Soham Banerjee
  • 465
  • 1
  • 8
  • 18
  • 5
    Is this inside an [tag:asp.net] application? If you want a page to change dynamically, you need to be writing javascript, not more code in your code-behind – Damien_The_Unbeliever May 07 '15 at 07:03

5 Answers5

3

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;
}
Adil
  • 146,340
  • 25
  • 209
  • 204
1

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Given the other code is running inside a method called `Page_Load` I suspect that this is asp.net code, in which case there isn't a UI thread and there are far bigger issues here. – Damien_The_Unbeliever May 07 '15 at 07:08
  • i get this error:- 'System.Web.UI.WebControls.Label' does not contain a definition for 'Invoke' and no extension method 'Invoke' accepting a first argument of type 'System.Web.UI.WebControls.Label' could be found (are you missing a using directive or an assembly reference?) – Soham Banerjee May 07 '15 at 07:14
  • @SohamBanerjee - Sorry, I assumed that this was Windows Forms. You need to write javascript to update the UI on web pages. – Enigmativity May 07 '15 at 07:18
1

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();
}));
M.Mahdipour
  • 592
  • 1
  • 7
  • 21
0

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.

Abhishek
  • 6,912
  • 14
  • 59
  • 85
  • It's not that c# doesn't allow it. It's Windows Forms/WPF that doesn't allow it. – Enigmativity May 07 '15 at 07:26
  • oh... It doesn't work on windows 8 and wp apps as well. So, basically on which ever thing I worked in c# didn't allowed me to update UI. It made me think it is c# standard way to handle UI. Thanks for awareness. Updating aanswer accordingly :) – Abhishek May 07 '15 at 07:31
0

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();
        }));
    }
insilenzio
  • 918
  • 1
  • 9
  • 23