Click here to Skip to main content
14,971,209 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm fairly new to C# and have recently started using windows form on Visual Studio. I created a countdown timer in winforms (for short) and I have encountered a problem. In this timer, I have two text boxes (one for entering the minutes and another for entering the seconds) with the names txt.Minutes and txt.Seconds, and one button to start the countdown. Whenever I press the button, the app just hangs (I cannot interact with anything inside the app). Here's the code for the button :
C#
private void btnStartTimer_Click(object sender, EventArgs e)
        {
            txtMinutes.ReadOnly = true;
            txtSeconds.ReadOnly = true;
            int a = Convert.ToInt32(txtMinutes.Text);
            int b = Convert.ToInt32(txtSeconds.Text);
            DateTime timer = new DateTime(2021, 4, 10, 0, a, b);
            DateTime timerB = new DateTime(2021, 4, 10, 0, 0, 0);
            TimeSpan OneSecond = new TimeSpan(0, 0, 1);
            TimeSpan OneMinute = new TimeSpan(0, 1, 0);
            TimeSpan FiftyNineSeconds = new TimeSpan(0, 0, 59);
            while (timer != timerB)
            {
                DateTime newTimer = new DateTime();
                //checks if the second on timerA is 0 or not
                if (timer.Second == 0)
                {
                    //if the second IS 0 then it subtracts 1 minute
                    newTimer = timer.Subtract(OneMinute);
                    if (timer.Minute != 0)
                    {
                        //if after subtracting 1 minute, the minute is not 0, it will add sixty seconds to the timer
                        to the timer
                        newTimer = timer.Add(FiftyNineSeconds);
                    }
                }
                //if the second on timerA is NOT 0...
                else if (timer.Second != 0)
                {
                    //it subtracts 1 second
                    newTimer = timer.Subtract(OneSecond);
                }
                //it then displays the minutes and seconds on the timer.
                txtMinutes.Text = Convert.ToString(newTimer.Minute);
                txtSeconds.Text = Convert.ToString(newTimer.Second);
                //it waits a 1000 milliseconds (1 second) for the next loop.
                Thread.Sleep(1000);

            }
            txtMinutes.ReadOnly = false;
            txtSeconds.ReadOnly = false;
        }


What I have tried:

I have tried using a BackgroundWorker but whenever I run it, an error occurs concerning cross threads. As a solution, I tried using .Invoke but I probably used it in a wrong format for which it still didn't work. Thats why in a last resort I came to this site.
Posted
Updated 16-Apr-21 6:07am

Thread.Sleep will cause the UI thread to sleep for 1 second. The UI will not update until your method returns.

Try using an async method instead:
C#
private void btnStartTimer_Click(object sender, EventArgs e)
{
    int minutes, seconds;
    if (!int.TryParse(txtMinutes.Text, out minutes))
    {
        txtMinutes.Focus();
        return;
    }
    if (!int.TryParse(txtSeconds.Text, out seconds))
    {
        txtSeconds.Focus();
        return;
    }
    
    var time = new TimeSpan(0, minutes, seconds);
    _ = RunTimer(time);
}

private async Task RunTimer(TimeSpan time)
{
    btnStartTimer.Enabled = false;
    txtMinutes.ReadOnly = true;
    txtSeconds.ReadOnly = true;
    try
    {
        TimeSpan tick = TimeSpan.FromSeconds(1);
        while (time > TimeSpan.Zero)
        {
            txtMinutes.Text = Convert.ToString((int)Math.Floor(time.TotalMinutes));
            txtSeconds.Text = time.Seconds.ToString();
            await Task.Delay(tick);
            time -= tick;
        }
    }
    finally
    {
        btnStartTimer.Enabled = true;
        txtMinutes.ReadOnly = false;
        txtSeconds.ReadOnly = false;
    }
}
   
Comments
chandrima dasgupta 17-Apr-21 12:24pm
   
I will notify you if your solution was helpful to creating the app later. Currently, I'm away from my PC. :)
When your app starts, a thread is created to start executing your code. This startup thread, also known as the UI thread, drives your applications message pump. The message pump is where Windows tells your app what to do, like redraw windows and controls, and what's going on, like your app receiving mouse and keyboard events. If you call Thread.Sleep on the UI thread, you are putting your entire application to sleep, including the processing of window drawing and mouse/keyboard events.

To keep your app "alive" while long-running work is being done, you have to move that work to another thread. This can be done through the use of Task, BackgroundWorker, or Thread. Sounds easy enough, right?

There is a catch. ONLY the UI thread can touch UI objects, like Forms, Buttons, Labels, TextBox's, ... So your work threads cannot touch any controls at all. Anything your threads have to send to UI controls, first, really needs to be thought through for need, and second, if required, an update to a UI control from a thread needs to be marshaled back to the UI thread so the UI thread can update the control. There's various ways to do this.

Having said all that, read the documentation on the Timer you're using. Chances are it's going to be the System.Windows.Forms.Timer class. That timer drops a message in your apps message pump, which will raise the Tick event on the UI thread, at an interval NO LESS THAN the number of milliseconds specified by the Timer's Interval property. Well, if you put the UI thread to sleep, the message that translates to raising the Tick event will also not get processed until Sleep expires, hence, your app, the the Timer, freezes.
   
Comments
chandrima dasgupta 17-Apr-21 12:24pm
   
I will notify you if your solution was helpful to creating the app later. Currently, I'm away from my PC. :)
chandrima dasgupta 18-Apr-21 9:43am
   
Could you list some ways to "send an update to a UI from a thread so that the UI can update the control?"
Dave Kreskowiak 18-Apr-21 11:40am
   
You want me to list some ... In this little textbox? Uhhh... no.

There is, however, a much better way. Google. Search for "C# marshal UI update" and start reading.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900