23

I'm new to C# and trying to create a simple countdown timer using System.Timer.Timers. It didn't work as expected and I searched the internet for a solution but it didn't really fix my problem. What I want is when the user clicks the start button, it begins and displays the countdown. But although the timer kinda worked, it didn't continuously display the timer when I clicked the button once, instead, I need to click the start button many times to see the countdown number or the timer display will not change. Here's the code.

@page "/"

<h1>Timer</h1>

<p>@counter</p>
<button @onclick="StartTimer">Start</button>


@code {
    private static System.Timers.Timer aTimer;
    private int counter = 60;
    public void StartTimer()
    {
        aTimer = new System.Timers.Timer(1000);
        aTimer.Elapsed += CountDownTimer;
        aTimer.Enabled = true;
    }

    public void CountDownTimer(Object source, System.Timers.ElapsedEventArgs e)
    {
        if (counter > 0)
        {
            counter -= 1;
        }
        else
        {
            aTimer.Enabled = false;
        }
    }
}
Reza Heidari
  • 1,192
  • 2
  • 18
  • 23
TheNoobProgrammer
  • 1,013
  • 4
  • 10
  • 21

1 Answers1

29

Call StateHasChanged() when updating the counter so that the UI elements are updated.

Because your callback will run on a separate thread you will need to use InvokeAsync to call StateHasChanged().

public void CountDownTimer(Object source, ElapsedEventArgs e)
{
    if (counter > 0)
    {
        counter -= 1;
    }
    else
    {
        aTimer.Enabled = false;
    }
    InvokeAsync(StateHasChanged);
}
Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • 3
    Don't forget to Dispose() the Timer. [Sample code](https://stackoverflow.com/a/63062755/60761) – H H Sep 05 '21 at 06:11
  • 2
    InvokeAsync(StateHasChanged) doesn't works, you shoud use InvokeAsync(() => StateHasChanged); – Payedimaunt Oct 23 '21 at 21:49
  • 2
    @Payedmaunt do you have more information on your test scenario? The overload of InvokeAction used here takes an Action delegate, which has a signature of `void ()` this matches the signature for `StateHasChanges` as well as the lambda you are passing. As it stands `InvokeAsync(StateHasChanged)` works, so there might be something you have different in your test case. – Chris Taylor Oct 25 '21 at 02:29
  • 1
    InvokeAsync(StateHasChanged) definitely works, its the same as doing InvokeAsync(() => StateHasChanged) since StateHasChanged has no parameters – Rob King Nov 06 '22 at 14:30