Динам. изменение стиля окна через API.

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
SergeySV
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 124
Зарегистрирован: 17.04.2003 (Чт) 14:39
Откуда: Россия, Москва

Динам. изменение стиля окна через API.

Сообщение SergeySV » 11.02.2004 (Ср) 10:59

Взялся за задачку создания 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 окном и наглядной демонстрации.
Вложения
db1.zip
Zip архив - mdb Access2000
(30.4 Кб) Скачиваний: 53
Главное двигаться не быстрее, чем думает твоя голова.

SergeySV
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 124
Зарегистрирован: 17.04.2003 (Чт) 14:39
Откуда: Россия, Москва

Re: Динам. изменение стиля окна через API.

Сообщение SergeySV » 11.02.2004 (Ср) 14:27

Мдам, кажись я всех загрузил :(
Главное двигаться не быстрее, чем думает твоя голова.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 12.02.2004 (Чт) 11:17

Да лень просто иногда...
Код: Выделить всё
SetWindowPos hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOOWNERZORDER
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

SergeySV
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 124
Зарегистрирован: 17.04.2003 (Чт) 14:39
Откуда: Россия, Москва

Сообщение SergeySV » 12.02.2004 (Чт) 17:38

GSerg писал(а):Да лень просто иногда...
Код: Выделить всё
SetWindowPos hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOOWNERZORDER


Токо что сам нашел в инете этот вариант. Притом достаточно случайно :)

вообщем корень зла находился в то, что я пропустил еще одно очень важное событие - WM_NCCALCSIZE - про него было написано, что оно вохникает при изменении клиентской части окна - вот я и прошел мимо него, не стал дальше разбираться, мне же нужно была НЕклиентская - старый осел :( ......

В этом сообщении передается указатель на структуру, в которой указывается размеры окна до и после изменения, размер клиентской счасти окна (до измен.) - Все очень просто, изменив стиль окна, убрав у него рамку, я должен был сообщить виндам что рамки нет и соотв. неклиентская часть тоже изменилась (ее не стало), изменилось правильно - за счет клиентской. Указываем в этой структуре, что клиентская часть занимает теперь все окно (добавляем флаги SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOOWNERZORDER) и посылаем сообщение WM_NCCALCSIZE.

Вообщем когда я искал инфу по WM_NCCALCSIZE я и наткнулся на выше указанный вариант, который делает тоже самое, всю эту громоздкую структуру он сам заполняет и направляет уже готовое сообщение WM_NCCALCSIZE.

Правда все равно не зря копался. Узнал для себя много нового. Благодаря этому WM_NCCALCSIZE можно отобрать у окна клиентскую часть окна и распоряжаться ей в свое усмотрение. Например видел модуль на VB, который через subclassing подключается к стан. TextBox, смещает у него клиентскую часть, а в неклиентской (слева) рисует номера строк.
Можно и просто таким макаром отодвинуть текст в TextBox подальше от края с точностью до пикселя, что тоже бывает иногда нужно.

Но все равно, спасибо за помощь и участие,....... может еще что придется спросить... ;)
Главное двигаться не быстрее, чем думает твоя голова.


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex-бот и гости: 7

    TopList