Писал тут код для реализации индикатора хода процесса. Индикатор поддерживает вращение вокруг оси. Для реализации вращения написал отдельный метод запускаемый конструктором класса в отдельном потоке. Назначение этого метода определённое количество раз в секунду вызывать метод Invalidate(). Выглядит этот метод примерно так:
//...время последнего вызова Invalidate()...private long _lastInvalidateTick = 0;
protected override void OnInvalidated(InvalidateEventArgs e)
{
base.OnInvalidated(e);
_lastInvalidateTick = DateTime.Now.Ticks;
}
void ReDrawBar()
{
//...количество выполнений метода в секунду...int timesPerSecond = 50;
while (_rotateThread.ThreadState == ThreadState.Running)
{
if (_lastInvalidateTick == 0)
{
Invalidate();
continue;
}
//...время прошедшее после последнего вызова Invalidate()...float elapsedTime = (DateTime.Now.Ticks - _lastInvalidateTick) / 10000000f;
if (elapsedTime > 1 / (timesPerSecond * 2f))
{
_currentRotateAngle += _rotateSpeed * elapsedTime;
Invalidate();
}
Thread.Sleep(1000 / timesPerSecond);
}
}
Прокомментирую.
if (elapsedTime > 1 / (timesPerSecond * 2f))
Эта условие не вызывает метод Invalidate() если он был вызван "недавно". То есть если метод выполняется 50 раз в секунду(каждые 0,02 секунды), а с момента последнего выполнения Invalidate() прошло времени меньше чем 0,02 секунды, то Invalidate() повторно не вызывается, т.к. в этом просто нет смысла, потому что индикатор и так был не давно перерисован.
И вроде все работает как надо, однако когда начал тестировать заметил одну особенность. Если метод вызывал Invalidate() 50 раз в секунду, то OnPaint() вызывался только 10-12 раз в секунду! Если количество вызовов метода увеличивалось до 100 раз в секунду ситуация не менялась, OnPaint() упорно вызывался с таким же промежутком. Это навело меня на мысль, что реализация Invalidate() специально вызывает OnPaint() не очень часто
Поэтому вопросы такие: Сколько раз в секунду вызывается OnPaint и, что этим управляет? Возможна ли ситуация, и когда, что OnPaint вызывается чаше чем в моем случае? Как повлиять на количество вызовов OnPaint за промежуток времени
Кстати сейчас провёл измерения и увидел, что если Invalidate() вызывается 100 раз в секунду, то OnPaint от 13 до 76 раз. А если Invalidate() вызывается 10 раз в секунду, то OnPaint от 5 до 9 раз В любом случае при разных значениях, OnPaint ни когда не вызывался больше раз чем Invalidate()!
Я помню, что Invalidate() запросы накапливаются и обрабатываются все разом. Вот только как мне управлять количеством вызовов OnPaint? Разместить в OnPaint код который будет вырубать все попытки вызвать метод больше определённого количества раз в секунду? Но в результате могут появиться рывки в движении индикатора. Может быть есть встроенные средства управления этим процессом?
И как вы думаете какое количество перерисовок индикатора в секунду оптимально? Не забываем, что он вращается, причём скорость вращения можно задавать разную!
Здравствуйте, Cynic, Вы писали:
C> Я помню, что Invalidate() запросы накапливаются и обрабатываются все разом. Вот только как мне управлять количеством вызовов OnPaint?
Control.Refresh();
Re[3]: Как реализован Invalidate ???
От:
Аноним
Дата:
10.05.08 10:38
Оценка:
Здравствуйте, Andy77, Вы писали:
C>> Я помню, что Invalidate() запросы накапливаются и обрабатываются все разом. Вот только как мне управлять количеством вызовов OnPaint? A>Control.Refresh();
Если я вызову Invalidate(), например 100 раз, сколько раз будет вызван OnPaint()? От чего это зависит? Понятно, что перемещении контрола он будет перерисовываться чаще, а если контрол не подвижен, от чего будет зависеть количество вызовов OnPaint()?
Зависит от того, насколько загружен Ваш поток. По крайней мере для Win32 GUI это верно (не знаю как для дотнета). Вообще, если Вам нужно точно обновлять контрол в данный момент времени пользуйтесь RedrawWindow (какой-то аналог этой функции в дотнет-мире должен быть). Очень хорошо о том как работает рисование и формируется WM_PAINT у Рихтера написано.
Здравствуйте, Аноним, Вы писали:
А> Если я вызову Invalidate(), например 100 раз, сколько раз будет вызван OnPaint()? От чего это зависит? Понятно, что перемещении контрола он будет перерисовываться чаще, а если контрол не подвижен, от чего будет зависеть количество вызовов OnPaint()?
Сообщение отрисовки WM_PAINT как таковое не является "настоящим" сообщением это фактически флажок говорщий о том, что у окна есть невалидные области. Поэтому, повторный вызов Invalidate не приведет к "двух кратному" вызову OnPaint. Данное сообщение обладает низким приоритетом — это означает, что обработано оно будет в "последнюю" очередь.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.