2

我试图让标签显示用户完成从 00:00:00 开始并从那里开始以毫秒为增量上升的任务所花费的时间。到目前为止,我有这个:

    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();在单击按钮时调用

有人可以指出我正确的方向吗?

4

3 回答 3

12

我建议采用 MVVM 方法。让您的 TextBlock 绑定到 ViewModel 上的字符串成员。在您的 ViewModel 中,您可以使用 DispatcherTimer 设置经过的时间。DispatcherTimer 在 UI 线程上触发其回调,因此您无需调用 UI 线程。

代码:

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}"/>
于 2012-04-19T02:20:02.503 回答
2

简单的方法是使用计时器(不是秒表)。计时器是一个组件,您可以设置间隔并在每个刻度上调用一个方法。例如,结合秒表的数据成员,您可以每 50 毫秒访问一次秒表的 Elapsed 属性(就像您在 ShowElapsedTime 方法中所做的那样)。

这样做的主要问题是计时器的计时不完美,并且在标签文本更新时也会出现颠簸。

另一种方法是使用线程来防止 UI 锁定,但是如果您从主线程以外的线程更改标签文本 - 您会遇到异常。

您可以绕过该异常,但更好的方法是使用 BackgroundWorker。

BGWorker 将在不同的线程中执行任务,您可以让它报告进度,以便在主线程中调用。

如果您真的想做到完美,请拥有一个实现 INotifyPropertyChanged 的​​类,该类具有属性 ElapsedTime 和私有 StopWatch 数据成员。该类将以下列方式使用 BackgroundWorker。

在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;
                              };

当您想启动工作人员(也就是启动计时器)时:

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

当您想停止工作人员(也就是停止计时器)时:

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

该类本身将具有与工作人员(数据成员)交互的 Start 和 Stop 方法。

最后,您可以将标签文本绑定到类的 ElapsedTime 属性。或者使用您的 ShowElapsedTime 方法订阅 ElapsedTimeChanged 事件,除非它将使用您的类的 ElapsedTime 属性而不是 stopWatch.Elapsed 属性。

于 2012-04-19T02:07:44.787 回答
1

您需要一个会ShowElapsedTime定期调用的计时器。有关示例,请参阅WPF 计时器倒计时。

于 2012-04-19T02:06:35.953 回答