Noelle
Noelle

Reputation: 782

Display the running time of part of a program in a label

I am trying to have a label display the time it takes the user to complete a task which starts at 00:00:00 and goes up in shown millisecond increments from there. So far I have this:

    private void startTimer()
    {
        stopWatch.Start();
        Dispatcher.BeginInvoke(DispatcherPriority.Render, new ThreadStart(ShowElapsedTime));
    }
    void ShowElapsedTime()
    {
        TimeSpan ts = stopWatch.Elapsed;
        lblTime.Text = String.Format("{0:00}:{1:00}.{2:00}", ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
    }

startTimer(); is called on a button click

Can someone point me in the right direction?

Upvotes: 2

Views: 13711

Answers (3)

Dan Busha
Dan Busha

Reputation: 3803

I'd recommend taking an MVVM approach. Have your TextBlock bind to a string member on your ViewModel. In your ViewModel you can use a DispatcherTimer to set the the time elapsed. The DispatcherTimer fires its callback on the UI thread so you don't need to invoke to the UI thread.

Code:

public class ViewModel : INotifyPropertyChanged
{
     public event PropertyChangedEventHandler PropertyChanged;
     public string TimeElapsed {get;set;}

     private DispatcherTimer timer;
     private Stopwatch stopWatch;

     public void StartTimer()
     {
          timer = new DispatcherTimer();
          timer.Tick += dispatcherTimerTick_;
          timer.Interval = new TimeSpan(0,0,0,0,1);
          stopWatch = new Stopwatch();
          stopWatch.Start();
          timer.Start();
     }



     private void dispatcherTimerTick_(object sender, EventArgs e)
     {
         TimeElapsed = stopWatch.Elapsed.TotalMilliseconds; // Format as you wish
         PropertyChanged(this, new PropertyChangedEventArgs("TimeElapsed")); 
     }
}

XAML:

<TextBlock Text="{Binding TimeElapsed}"/>

Upvotes: 13

YoryeNathan
YoryeNathan

Reputation: 14522

The simple way would be using a Timer (not a stopwatch). The timer is a component which you can set intervals of and invoke a method on each tick. Combine with a data member of a stopwatch and you can access the stopwatch's Elapsed property (as you do in your ShowElapsedTime method) every 50 milliseconds, for example.

The main problem with that would be that the timer isn't perfectly timed and will also be bumpy on the label text update.

A different approach would be using a thread to prevent the UI from locking down, but then if you changed label text from a thread other than your main thread - you get an exception.

You CAN bypass that exception, but the better way would be using a BackgroundWorker.

The BGWorker will perform the task in a different thread and you can let it report progress, to be invoked in the main thread.

If you really want to be perfect about it, have a class that implements INotifyPropertyChanged that has a property ElapsedTime, and a private StopWatch data member. The class will use a BackgroundWorker in the following manner.

At the ctor:

this._stopwatch = new Stopwatch();
this._worker = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};

_worker.DoWork += (s, e) =>
                     {
                         while (!_worker.CancellationPending)
                         {
                             _worker.ReportProgress(0, watch.Elapsed);
                             Thread.Sleep(1);
                         }
                     };

_worker.ProgressChanged += (s, e) =>
                              {
                                  this.ElapsedTime = (TimeSpan)e.UserState;
                              };

When you want to start the worker (aka start the timer):

stopwatch.Start();
_worker.RunWorkerAsync();

And when you want to stop the worker (aka stop the timer):

stopwatch.Reset();
_worker.CancelAsync();

The class itself will have a Start and Stop methods that will interact with the worker (data member).

Finally, you can BIND the label text to your class's ElapsedTime property. Or subscribe to an ElapsedTimeChanged event with your ShowElapsedTime method, except it will then use your class's ElapsedTime property instead of the stopWatch.Elapsed property.

Upvotes: 2

Jim Mischel
Jim Mischel

Reputation: 134045

You need a timer that will call ShowElapsedTime periodically. See WPF timer countdown for an example.

Upvotes: 1

Related Questions