2012-04-19 5 views
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(); вызывается нажатием кнопки

Может кто-нибудь указать мне в правильном направлении?

ответ

11

Я бы рекомендовал использовать подход MVVM. Свяжите свой TextBlock с членом строки на ViewModel. В ViewModel вы можете использовать DispatcherTimer для установки прошедшего времени. DispatcherTimer запускает обратный вызов в потоке пользовательского интерфейса, поэтому вам не нужно ссылаться на поток пользовательского интерфейса.

Код:

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}"/> 
+1

Нет ли вызов таймера остановки требуется? не будет ли это вечно? – batmaci

+0

OP спрашивал, как отображать время на ярлыке, поэтому мой ответ был сфокусирован на этом. Когда условие остановки достигнуто для активности (нажатие кнопки, ввод пользователя и т. Д.), Вам необходимо сделать вызов таймера.Stop() https://msdn.microsoft.com/en-us/library/system.windows .threading.dispatchertimer.stop (v = vs.110) .aspx –

+0

Спасибо за разъяснение. ваш код тоже помог мне, но я хотел бы привязать его к другой viewmodel, которая является datacontext моего представления. Можете ли вы дать мне подсказку, как я могу связать их обоих? Так что, хотя datagrid связан другой наблюдаемой моделью viewmodel, я могу активировать таймер в качестве секций загрузки, точно так же, как таймер в студии управления SQL SQL при выполнении запроса. Если это так сложно, я открою новый поток. большое спасибо. – batmaci

1

Вам нужен таймер, который будет называть ShowElapsedTime периодически. См. Пример WPF timer countdown.

2

Простым способом будет использование таймера (а не секундомера). Таймер - это компонент, который вы можете установить интервалы и вызвать метод для каждого тика. Объедините с элементом данных секундомера, и вы можете получить доступ к свойству Elapsed секундомера (как и в методе ShowElapsedTime) каждые 50 миллисекунд.

Основная проблема заключается в том, что таймер не совсем синхронизирован, а также будет неровным по обновлению текста ярлыка.

Другой подход - использовать поток, чтобы предотвратить блокировку пользовательского интерфейса, но если вы изменили текст метки из потока, отличного от основного потока, вы получите исключение.

Вы можете обойти это исключение, но лучшим способом будет использование 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(); 

У самого класса будут методы «Старт и Стоп», которые будут взаимодействовать с работником (членом данных).

Наконец, вы можете НАПРАВИТЬ текст метки в свой класс ElapsedTime. Или подписаться на событие ElapsedTimeChanged с помощью метода ShowElapsedTime, за исключением того, что вместо него будет использоваться свойство ElapsedTime вашего класса, а не свойство stopWatch.Elapsed.