реализация секундомера в wp7

В моем приложении wp7.5 у меня есть блок, в котором мне нужно показать секундомер (скажем, 30,29,28...1,0). Я пробовал различные реализации для достижения этого, используя классы DispatchTimer и Timer, но ни одна из них не решила мою проблему.

Подход 1: вот фрагмент, который я использовал для DispatchTimer,

DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 1); // 1 second
dt.Tick += new EventHandler(dt_Tick);

for(int count=0;count<30;count++)
    dt.Start();

void dt_Tick(object sender, EventArgs e)
{
   // my UI control update here
}

в моей реализации события Tick я обновляю элемент управления пользовательского интерфейса со счетчиком времени. Я прочитал здесь несколько вопросов по той же теме, где галочка диспетчера никогда не срабатывает в некоторых сценариях из-за потока пользовательского интерфейса. Со мной случилось то же самое, событие тика никогда не срабатывало.

Подход 2: я попытался использовать класс System.Threading.Timer,

Timer timer = new Timer(TimerProc);

for(int count=0;count<30;count++)
    timer.Change(1000, 0);

void TimerProc(object sender)
{
   // my UI control update here
}

Ни один из моих подходов не работал. У меня может быть повторный вопрос, может ли кто-нибудь указать мне, где я делаю неправильно в коде?


person Mahender    schedule 31.12.2011    source источник


Ответы (2)


DispatcherTimer после метода Start(), называемого keep, запускает Tick event по истечении интервала до тех пор, пока вы не вызовете для него Stop(). Таким образом, вам не нужно вызывать Start() 30 раз, но вы должны поддерживать счетчик и в обработчике Tick event останавливать таймер после 30 тиков:

private int counter;

private void Start_Click(object sender, RoutedEventArgs e)
{
    counter = 30;
    DispatcherTimer dt = new DispatcherTimer();
    dt.Interval = new TimeSpan(0, 0, 1); // 1 second
    dt.Tick += new EventHandler(dt_Tick);
    dt.Start();
}

void dt_Tick(object sender, EventArgs e)
{
    if (counter >= 0)
    {
        timeText.Text = counter.ToString();
        counter--;
    }
    else
        ((DispatcherTimer)sender).Stop();
}

Изменить:
Если точности DispatcherTimer недостаточно, вы можете использовать System.Threading.Timer, но в этом случае у вас есть вызов Dispatcher.BeginInvoke в обработчике событий тика, чтобы разрешить доступ к объектам в потоке пользовательского интерфейса:

private int counter;

private void Start_Click(object sender, RoutedEventArgs e)
{
    counter = 30;
    timeText.Text = counter.ToString();
    Timer dt = new Timer(dt_Tick);
    dt.Change(1000 /* delay to start the timer */, 1000 /* period time */);
}

private void dt_Tick(object sender)
{
    if (counter > 0)
    {
        Dispatcher.BeginInvoke(() => timeText.Text = counter.ToString());
        counter--;
    }
    else
        ((Timer) sender).Dispose();
}
person nemesv    schedule 31.12.2011
comment
Спасибо за ответ, но я заметил проблему с этим подходом. Если я использую интервал 20 секунд, на самом деле цикл до 24 секунд. Есть ли способ сделать это точным? - person Mahender; 31.12.2011

Метод Timer tick не должен отслеживать время. Почему? По двум причинам.

1/Интервал между каждым тиком DispatcherTimer не является точным и зависит от того, что вы делаете с пользовательским интерфейсом. Это может быть 1 секунда, как это может быть немного больше.

2/ Следующий тик запускается через одну секунду после окончания предыдущего тика. Поскольку код обновления внутри галочки занимает некоторое время, он автоматически будет отставать. Например, если код обновления занимает 0,1 секунды, тик срабатывает через 1 секунду, обновление выполняется, а следующий тик происходит через 1 секунду после окончания обновления, то есть через 2,1 секунды после запуска таймера!

Следовательно, вы должны сохранить время, когда вы запустили таймер, и вычислить прошедшее время на каждом тике:

    protected DispatcherTimer Timer { get; set; }

    protected DateTime TimerStartTime { get; set; }

    private void ButtonStart_Click(object sender, RoutedEventArgs e)
    {
        this.Timer = new DispatcherTimer();
        this.Timer.Interval = TimeSpan.FromSeconds(1);
        this.Timer.Tick += this.Timer_Tick;
        this.TimerStartTime = DateTime.Now;
        this.Timer.Start();
    }

    private void Timer_Tick(object sender, EventArgs e)
    {
        int elapsedSeconds = (int)DateTime.Now.Subtract(this.TimerStartTime).TotalSeconds;

        this.TextTimer.Text = (30 - elapsedSeconds).ToString();
    }

Таким образом, даже если время между каждым тиком не является точным, таймер не будет указывать ложное время. Вы даже можете уменьшить интервал таймера до 500 мс или меньше, чтобы иметь более точный таймер, не меняя метод Tick.

person Kevin Gosse    schedule 31.12.2011