![]() |
Модераторы: Poseidon, Snowy, bems, MetalFan |
![]() ![]() ![]() |
|
Poseidon |
|
|||
![]() Delphi developer ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 5273 Регистрация: 4.2.2005 Где: Гомель, Беларусь Репутация: 53 Всего: 133 |
ТЕОРИЯ:
Предисловие: Недавно поднималась тема о прозрачности окна и непрозрачности компонентов. Решения были разные. Представляю вашему вниманию еще одно решение с улучшением- наложением светофильтров. Окно программы будет не просто прозрачным, но и цветным. Прежде всего, хочу предупредить, что этот текст рассчитан на людей, знакомых с функциями WinAPI и сообщениями Windows и поэтому вряд ли будет полезен тем, кто предпочитает ограничиваться работой на уровне компонентов Delphi. Теперь ещё два предупреждения: во первых, полупрозрачное окно иногда перерисовывается не совсем правильно. Во-вторых, эта программа написана только для демонстрации самого принципа создания полупрозрачных окон и кое-что в ней можно (и иногда даже нужно) улучшить. Внутренняя схема перерисовки окон в Windows такова, что каждый раз перерисовывается только та его часть, которая не закрыта другими окнами и, следовательно, видна на экране. Это ускоряет процесс обновления экрана. Но полупрозрачное окно должно каким-то образом получать информацию о том, что нарисовано под ним. Здесь мы вступаем в противоречие с правилами Windows, поэтому следует прибегнуть к некоторому обману. При необходимости перерисовать полупрозрачное окно надо это окно ненадолго убрать с экрана. Как только все нижележащие окна будут перерисованы, надо запомнить ту область экрана, которая будет закрыта окном, вновь вывести на экран это окно и отрисовать его с учётом сохранённой картинки. Первая трудность на этом пути - как узнать, что перерисовка всех окон закончилась? Самый примитивный способ- подождать! Скажу только, что времени задержки 400 мс, которое установлено в этом примере вполне хватило. Вторая трудность - если вдруг окно, лежащее под полупрозрачным, обновилось, то это не приведёт сразу же к перерисовке этого окна. Теперь о том, как осуществить всё это на практике. Перерисовка окна определяется обработкой одного из двух сообщений: WM_EraseBkgnd и WM_Paint. WM_Paint лучше вот почему: Во-первых, WM_EraseBkgnd может посылаться по несколько раз, соответственно окно перерисовывается до нескольких раз подряд. Во-вторых, между WM_EraseBkgnd и WM_Paint есть существенная разница: в первом случае Windows сам определяет, какая часть окна должна перерисоваться, и рисовать за пределами этой части просто не разрешает. А полупрозрачное окно, вообще говоря, обладает нетрадиционной точкой зрения на этот вопрос, что и приводит к конфликтам, особенно тогда, когда полупрозрачное окно частично закрыто другими окнами. Что же касается WM_Paint, то и тут Windows, конечно же, держит всё под контролем и тоже следит за тем, какая область окна должна быть перерисована. Однако, к счастью для полупрозрачных окон, это всё не выливается в прямые запреты, как в случае с WM_EraseBkgnd, здесь Windows ограничивается только выдачей ценных указаний через BeginPaint и TPaintStruct. Ну, а теперь мы отрисовываем окно целиком. И наконец, в-третьих, Windows зачем-то генерирует WM_EraseBkgnd после выполнения ShowWindow, поэтому попытка спрятать окно при обработке этого сообщения приведёт к бесконечной рекурсии. Впрочем, пренебрегать WM_EraseBkgnd тоже не стоит. Дело в том, что во время запуска программы этому процессу, видимо, присваивается повышенный приоритет. Это приводит к тому, что наше окно начинает рисоваться, Windows посылает WM_EraseBkgnd, стандартная процедура обработки этого события закрашивает всю клиентскую часть окна красивым серым цветом (как обычно), затем обрабатывается WM_Paint, в котором окно прячется с экрана, после чего остальные окна должны быстро перерисоваться и, когда пройдёт заданное время, программа посмотрит, что там нарисовано, начнёт наложение светофильтра... А окна-то не успели перерисоваться! Программа увидит своё собственное окно, которое мы не успели стереть. Это нам не надо, поэтому, чтобы не увеличивать время ожидания, нужно заблокировать вызов стандартного обработчика WM_EraseBkgnd. Это, естественно, никак не отразится на скорости перерисовки остальных окон, но ведь и само окно ничего не нарисует на экране, и после ожидания программа увидит то, что надо. И последний штрих: при перемещении окна Windows, во избежание ненужных действий, не запускает механизм перерисовки, а просто переносит изображение с одного места на другое. Для полупрозрачных окон это недопустимо, изображение должно обновляться при каждом переносе. Для этого надо отслеживать сообщение WM_Move, которое возникает в таких случаях. И, соответственно, запускать перерисовку окна. Если WM_Move вам не подходит , вы можете использовать WM_WindowPosChanged. Я не заметил разницы... Проблема заключается в том, что в некоторых версиях системы при перемещении окна не рисуется рамка, а каждый раз происходит перерисовка всего окна целиком. То же самое происходит и при изменении размеров окна. Так происходит, например, в Win NT и в Win95 при установленном MS Plus! Ключ к решению проблемы лежит в обработке сообщений WM_EnterSizeMove и WM_ExitSizeMove. Нужно завести переменную типа boolean, которая будет изменяться при начале перетаскивания с False на True и наоборот при его завершении. Соответственно обработчик WM_Paint должен следить за этой переменной и не выполнять задержку. Проблема - как узнать, что должно быть под окном. Если каждый раз запоминать не только нужную часть экрана, а весь экран целиком, то к моменту входа в режим перетаскивания программа будет обладать всей необходимой информацией о том, что там внизу. Теперь надо будет только вырезать нужный кусок. Главный недостаток такого подхода - программа должна быть, как пионер, всегда готова к началу перетаскивания и всегда сохранять экран целиком, что приведёт к дополнительному расходу памяти. Но от этого тоже можно избавиться, храня только нужный кусочек экрана (пока не реализовано). ПРАКТИКА: Бросим на форму Button, SpinEdit, ColorDialog. Потом приводим код к такому состоянию:
Приведенный код без комментариев. Они все в присоединенном файле. Дерзайте. Послесловие: Тут довольно много некорректного кода (по причине того, что я много еще не знаю). Просьба все улучшения выкладывать в этой теме (без флейма, плз). В примере добавлены два label`а, чтобы показать, что их свойство Transparent работает. И еще- попробуйте в SpinEdit добавить ноликов, чтобы значение выходило за «100», будет красиво! Помогли- DelphiKingdom -------------------- Если хочешь, что бы что-то работало - используй написанное, если хочешь что-то понять - пиши сам... |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Delphi: Общие вопросы" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |