Взялся за задачку создания popup окна и управления им средствами API. Все это понадобилось для Access, ну знаете чтобы типа Hinta висело с текстом и другой ерундой. Написал свой маленький класс реализующий управление окном.
Но беда подкралась незаметно. Захотел я дать возможность пользователям динамически менять стиль окна, а именно border, т.е. убирать рамку у окна или наоборот. И вот тут выяснилось что после изменения стиля окна, я никак не могу его культурными средствами перерисовать, чтобы убрать с глаз долой рамку.
Симпотоны: после изменения стиля окна (удаляем у него стиль WS_BORDER), пиксели рамки остаются как есть - черными. Кидаем поверх чужое окно, убираем и видим - все наше окно перерисовалось, а в этой рамке остались чужие пиксели. т.е. получаются какие-то неприкаенные краевые НИЧЕЙНЫЕ пиксели. Если окно через MoveWindow передвинуть, то рамку окрасится в белый(а может и в серый) цвет и переедет за окном.
Единственный способ которым я это одолел - это изменение размеры окна. Достаточно на 1 пиксель увеличить/уменьшить ширину/высоту окна и все ТИП-ТОР.
Мои рассуждения:
С помощью Spy++ я посмотрел к каким сообщениям приводит - MoveWindow xOld,yOld, Width+1,HeightOld (т.е. изменили только ширину окна):
WM_WINDOWPOSCHANGING
WM_NCCALCSIZE
WM_NCPAINT (с указанием региона)
WM_ERASEBKGND
WM_WINDOWPOSCHANGING
WM_MOVE
WM_SIZE (новый размер)
WM_WINDOWPOSCHANGING
WM_PAINT
Если же мы просто передвинем окно не меняя его размера, то получим:
WM_WINDOWPOSCHANGING
WM_MOVE
WM_PAINT
Т.о. насколько я правильно понял прочитав справку именно WM_NCPAINT - заставляет перерисовать исчезнувшую рамку окна.
Естественно, что ни InvalidateRect и UpdateWindow не могут обновить нормально окно, потому что перерисовывают клиентску часть (генерят WM_PAINT), а не WM_NCPAINT.
Я пробывал напрямую посылать WM_NCPAINT через SendMessage или использовать RedrawWindow (которую можно заставить перерисовать все), но у меня ничего не вышло.
Т.е. через Spy++ я конечно отследил что окно получает посланное мое сообщение WM_NCPAINT, но не реагирует на него так, как если бы просто менял размеры окна.
Насколько я понял загвоздка в wParam параметре сообщения WM_NCPAINT. Тут по справке нужно передать иден. региона или 1, если все перерисовать. Я пробовал и 0 и 1 и все без толку. А вот когда меняешь размеры окна через MoveWindow, то там через Spy++ видно что всегда в wParam WM_NCPAINT передается какой-то конкретный регион, но вот что это за регион и откуда его брать???
Решил зделать так:
hdc = GetWindowDC(vchWnd)
hRg = CreateRectRgn(20, 20, 50, 20) ' соотв. размерам окна
Call SelectClipRgn(hdc, hRg)
Call SendMessage(vchWnd, WM_NCPAINT, hRg, 0&)
т.е. создал свой регион с размерами окна и его подсунул - НИФИГА.
Мне тут ссылку подкинули:
"Биты стиля обычно отражают текущее состояние окна, но изменение стиля при помощи функции SetWindowLong не приводит к соответствующему изменпнию окна (по крайней мере, не сразу). Некоторые биты стиля могут успешно изменяться во время работы программы, однако изменение большинства битов правльно действует лишь при создании окна... ...В документации Microsoft не сказано, какие биты стиля могут изменяться во время работы программы; следовательно, вы должны изменять их на свой страх и риск и только после тщательных экспериментов". (С) Дан Эпплман
Но судя по моим экспериментам, изменение рамки у окна возможно, по крайне мере через MoveWindow это получается сделать корректно. Поэтому моя задача сводится просто к нахождению более приемливого решения. - Можно конечно использовать двойной вызов MoveWindow, но хочется оптимизировать алгоритм. По ходу MoveWindow вызывается много лишних событий, тогда как решающее для моей задачи является только одно (WM_NCPAINT). Задача за малым, повторить его...
Я тут накатал примерчик для показа всего этого безобразия. Это mdb-файл Access. Там на форме выведены кнопки для управления этим Popup окном и наглядной демонстрации.