Search code examples
c#winformstimerstopwatch

Having trouble updating label with elapsed stopwatch time


I want to update a label every so often with the elapsed time it takes a method to complete. I created a stopwatch to get elapsed time, and a windows forms timer to update the label with the elapsed time via the Tick event. I put together a short example of my issue, please see below.

using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;

namespace SOWinFormsTest
{
    public partial class Form1 : Form
    {
        private static Stopwatch watch = new Stopwatch();
        private static System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer.Interval = 1000;
            timer.Tick += timer_Tick;
            timer.Start();

            watch.Start();
            SomeMethod();
            watch.Stop();

        }
        private void timer_Tick(Object sender, EventArgs e)
        {
            label1.Text = GetTimeString(watch.Elapsed);
        }

        private string GetTimeString(TimeSpan elapsed)
        {
            string time = string.Empty;
            time = string.Format("{0:00}:{1:00}:{2:00}.{3:000}",
                elapsed.Hours,
                elapsed.Minutes,
                elapsed.Seconds,
                elapsed.Milliseconds);

            return time;
        }

        // Mimicking a method that takes 5 seconds to complete 
        // My actual code could take a lot longer
        private void SomeMethod()
        {
            Thread.Sleep(5000);           
        }
    }
}

When I run this program, and click my button, the label updates only once and that is after the 5 seconds it takes SomeMethod() to complete. I would like the label to update every second with the elapsed time. Does anyone know why this isn't working as intended? Is this a multi-thread issue? Do I need a BackgroundWorker? Or am I missing something over my head entirely?


Solution

  • You're executing your SomeMethod on the main GUI thread. This stops message pumping. .NET WinForms are not doing a great job of hiding Windows internals, so this becomes an issue.

    In short, a GUI thread works in a "infinite loop" and processes messages from Windows, which include any GUI updating messages, timer messages, user inputs, etc. (messages may come not only from Windows, but this is unrelated to this question).

    This is mentioned here, assuming you're using .NET Framework 4.8: Timer class.

    If you want to measure execution time of a method, you must run it in separate thread to not block the GUI thread and to get more accurate results (since GUI processing on the main thread won't mess with the execution time as much).