понедельник, 3 января 2011 г.

Существует реализация WM_SETREDRAW по умолчанию, но вы можете сделать и лучше

Это перевод There's a default implementation for WM_SETREDRAW, but you might be able to do better. Автор: Реймонд Чен.

Если ваше окно не имеет обработчика для сообщения WM_SET­REDRAW, то Def­Window­Proc даст вам поведение по умолчанию, которое подавляет сообщения WM_PAINT для вашего окна, пока перерисовка отключена, и начинает пропускать WM_PAINT (плюс вызывает полное обновление), когда перерисовка снова включается (внутренне это реализуется, делая окно псевдо-невидимым, но это деталь реализации, на которую вам не нужно ориентироваться).

Хотя реализация по умолчанию работает отлично для простых элементов управления, более сложные элементы управления могут делать эту работу и лучше - и, вообще-то, они должны делать лучше, ибо в этом и заключается смысл WM_SET­REDRAW.

Предполагаемое использование отключение перерисовки окна заключается в выполнении пакета действий над окном за раз, когда вы не хотите тратить время на обновление окна после каждого мельчайшего изменения, поскольку оно тут же устареет. К примеру, если вы хотите добавить сотню элементов в ListBox, то вы не хотите выполнять при этом 100 перерисовок экрана, если вам достаточно только одной. Возможно, вы видели программы, которые забывают заблокировать перерисовку в такие моменты: приложение замирает, кроме этого ListBox-а, кнопка (thumb) прокрутки которого медленно уменьшается по мере заполнения списка.

Я скажу, что операции подобного плана и есть цель WM_SET­REDRAW для сложного элемента управления, потому что если у вас есть простой элемент управления (вроде кнопки), то существует не много таких операций, которые вы можете сделать с кнопкой, так что в блокировке перерисовки кнопки есть не много смысла.

Например, ListView имеет свой обработчик WM_SET­REDRAW, который устанавливает флаг "обновление отключено". Другой код ListView проверяет этот флаг и пропускает сложные вычисления. К примеру, когда вы добавляете новый элемент в список, то ListView не пересчитывает размеры и положение полосы прокрутки, если его перерисовка отключена; он просто устанавливает внутренний флаг "Когда перерисовку включат - надо пересчитать полосу прокрутки". Если ListView находится в режиме автоупорядочивания, то он не тратит время на вычисление новых позиций элементов при удалении или добавлении; он просто устанавливает аналогичный внутренний флаг. А когда наконец вы включаете перерисовку обратно, то обработчик WM_SET­REDRAW видит эти флаги-напоминания и говорит: "OK, давайте разберёмся со всем этим". Таким образом, если вы добавляете 100 элементов, то вы не выполняете 99 бесполезных вычислений положения полосы прокрутки и 99 бесполезных вычислений положений элементов. Поскольку некоторые из подобных операций имеют стоимость O(n), то откладывая их, вы улучшаете производительность вставки с O(n²) до O(n).

Мораль истории: если у вас есть элемент управления, который управляет большим количеством элементов, то вам лучше бы иметь обработку WM_SET­REDRAW, чтобы сделать пакетные обновления эффективными.

Бонус-болтовня: заметьте, что использование Lock­Window­Update в качестве заменителя WM_SET­REDRAW не даст вам этой оптимизации. Несмотря на то, что окно блокировано для обновления, оно всё ещё перерисовывает себя, даже хотя вы не видите результатов - приводя к плате полной стоимости O(n²).

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.