Entry tags:
WM_SETREDRAW - оптимизация мелькания окон
(пост для программистов)
Наткнулся здесь на одну неочевидную фишку - может, "виндузятникам" пригодится. Сам по себе прием достаточно известный, но вот подводные камни, которые там обнаруживаются...
Бывает так, что необходимо сделать много изменений в каком-нибудь контроле. Например, "напихать" в List Box 9000 элементов. Тогда окно "лочится", делаются изменения, а потом оно "разлочивается" и перерисовывается один раз. Обычно рекомендуется так:
SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
-- здесь вносятся изменения --
SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
InvalidateRect(hwnd, NULL, TRUE);
Сейчас неважно, как там все устроено внутри системы, но эксперимент показывает, что при больших объемах изменений, ускорение получается значительное. Ту же технику можно использовать и для окон собственного изготовления: в "залоченном" состоянии не пытаться делать лишних телодвижений, например не пересчитывать строку максимальной ширины.
А теперь о подводных камнях.
Я здесь пишу на Platform SDK, но во многих библиотеках-"обертках" принцип схожий и могут возникать аналогичные глюки.
Первый камень - иногда изменения затрагивают frame, а InvalidateRect требует перерисовки только клиентской области. Так что рамку и скроллеры могут оказаться подзагаженными. Поэтому, если ваше окошко имеет рамку, лучше добавить еще:
RedrawWindow(handle, NULL, NULL, RDW_FRAME|RDW_INVALIDATE);
Вторая подлянка связана с оптимизацией. Можно вызывать перерисовку, только если знаете, что реально что-то поменяли (чтобы меньше мелькало). Любое изменение взводит флаг, а в конце вызывается перерисовка, только если изменения были.
Подлянка возникает из-за того, что WM_SETREDRAW убивает предыдущие invalidate-ы. И может возникнуть такая ситауция:
lock
- что-то изменили, установили флаг = true
unlock
invalidate
флаг = false
lock
- ничего не меняли, флаг = false
unlock
- ничего не вызываем, так как флаг = false
Второй lock отменяет invalidate, так что перерисовки не будет. А должна быть. Вот как это решается. Перед lock надо проверить, не установлено ли уже требование перерисовки. Если установлено, то поднять флаг:
if (GetUpdateRect(handle, NULL, FALSE))
флаг = true
И третья подлянка. Эта техника несовместима с видимостью окна. Если окно невидимое, технику применять нельзя, так как второй WM_SETREDRAW сделает окно видимым. Invalidate проблем не создает.
Наткнулся здесь на одну неочевидную фишку - может, "виндузятникам" пригодится. Сам по себе прием достаточно известный, но вот подводные камни, которые там обнаруживаются...
Бывает так, что необходимо сделать много изменений в каком-нибудь контроле. Например, "напихать" в List Box 9000 элементов. Тогда окно "лочится", делаются изменения, а потом оно "разлочивается" и перерисовывается один раз. Обычно рекомендуется так:
SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
-- здесь вносятся изменения --
SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
InvalidateRect(hwnd, NULL, TRUE);
Сейчас неважно, как там все устроено внутри системы, но эксперимент показывает, что при больших объемах изменений, ускорение получается значительное. Ту же технику можно использовать и для окон собственного изготовления: в "залоченном" состоянии не пытаться делать лишних телодвижений, например не пересчитывать строку максимальной ширины.
А теперь о подводных камнях.
Я здесь пишу на Platform SDK, но во многих библиотеках-"обертках" принцип схожий и могут возникать аналогичные глюки.
Первый камень - иногда изменения затрагивают frame, а InvalidateRect требует перерисовки только клиентской области. Так что рамку и скроллеры могут оказаться подзагаженными. Поэтому, если ваше окошко имеет рамку, лучше добавить еще:
RedrawWindow(handle, NULL, NULL, RDW_FRAME|RDW_INVALIDATE);
Вторая подлянка связана с оптимизацией. Можно вызывать перерисовку, только если знаете, что реально что-то поменяли (чтобы меньше мелькало). Любое изменение взводит флаг, а в конце вызывается перерисовка, только если изменения были.
Подлянка возникает из-за того, что WM_SETREDRAW убивает предыдущие invalidate-ы. И может возникнуть такая ситауция:
lock
- что-то изменили, установили флаг = true
unlock
invalidate
флаг = false
lock
- ничего не меняли, флаг = false
unlock
- ничего не вызываем, так как флаг = false
Второй lock отменяет invalidate, так что перерисовки не будет. А должна быть. Вот как это решается. Перед lock надо проверить, не установлено ли уже требование перерисовки. Если установлено, то поднять флаг:
if (GetUpdateRect(handle, NULL, FALSE))
флаг = true
И третья подлянка. Эта техника несовместима с видимостью окна. Если окно невидимое, технику применять нельзя, так как второй WM_SETREDRAW сделает окно видимым. Invalidate проблем не создает.
no subject
no subject
no subject
(Anonymous) 2009-11-12 06:29 am (UTC)(link)