Непонятный класс окна - STATIC
Выбрал я для начала класс - "STATIC"
Создаю окно (код на VB, но суть от этого не менятся):
vchWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "STATIC", "привет", WS_POPUP Or WS_CHILD Or WS_BORDER, 0, 0, 50, 20, hWndApp, 0, hInsApp, ByVal 0&)
ShowWindow vchWnd, SW_NORMAL
полученное окошко нормально отображается/прячется, НО я не могу его двигать через MOVEWINDOW. Почитав все что смог найти про класс STATIC я так и не понял, запрещает ли он изменение своего положение и размеров или нет.
Тогда я создал собственный класс:
Dim wndcls As WNDCLASS
wndcls.style = CS_HREDRAW + CS_VREDRAW
wndcls.lpfnwndproc = GetWindowLong(hWndApp, GWL_WNDPROC)
wndcls.cbClsextra = 0
wndcls.cbWndExtra2 = 0
wndcls.hInstance = GetWindowLong(hWndApp, GWL_HINSTANCE)
wndcls.hIcon = 0
wndcls.hCursor = 0
wndcls.hbrBackground = COLOR_WINDOW
wndcls.lpszMenuName = 0
wndcls.lpszClassName = "MyWinPopup"
vchWnd = RegisterClass(wndcls)
vchWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "MyWinPopup", "привет", WS_POPUP Or WS_CHILD Or WS_BORDER, 0, 0, 50, 20, hWndApp, 0, hInsApp, ByVal 0&)
Полученное окно появляется БЕЗ текста и ТОЖЕ не хочет реагировать на MoveWindow (да еще и вылетает потом при закрытии окна).
Вообщем я запутался, в чем может быть трабл??? почему такие нехорошие окна получаются, что никак не хотят двигаться, дело может в комбинации WS_POPUP Or WS_CHILD и WS_EX_TOOLWINDOW?
Цитата:
Originally posted by SergeySV
Необходимо мне создать маленькое дочернее popup окошко, чтобы была возможность его и передвигать и текст менять.
Выбрал я для начала класс - "STATIC"
Создаю окно (код на VB, но суть от этого не менятся):
vchWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "STATIC", "привет", WS_POPUP Or WS_CHILD Or WS_BORDER, 0, 0, 50, 20, hWndApp, 0, hInsApp, ByVal 0&)
ShowWindow vchWnd, SW_NORMAL
полученное окошко нормально отображается/прячется, НО я не могу его двигать через MOVEWINDOW. Почитав все что смог найти про класс STATIC я так и не понял, запрещает ли он изменение своего положение и размеров или нет.
Тогда я создал собственный класс:
Dim wndcls As WNDCLASS
wndcls.style = CS_HREDRAW + CS_VREDRAW
wndcls.lpfnwndproc = GetWindowLong(hWndApp, GWL_WNDPROC)
wndcls.cbClsextra = 0
wndcls.cbWndExtra2 = 0
wndcls.hInstance = GetWindowLong(hWndApp, GWL_HINSTANCE)
wndcls.hIcon = 0
wndcls.hCursor = 0
wndcls.hbrBackground = COLOR_WINDOW
wndcls.lpszMenuName = 0
wndcls.lpszClassName = "MyWinPopup"
vchWnd = RegisterClass(wndcls)
vchWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "MyWinPopup", "привет", WS_POPUP Or WS_CHILD Or WS_BORDER, 0, 0, 50, 20, hWndApp, 0, hInsApp, ByVal 0&)
Полученное окно появляется БЕЗ текста и ТОЖЕ не хочет реагировать на MoveWindow (да еще и вылетает потом при закрытии окна).
Вообщем я запутался, в чем может быть трабл??? почему такие нехорошие окна получаются, что никак не хотят двигаться, дело может в комбинации WS_POPUP Or WS_CHILD и WS_EX_TOOLWINDOW?
Необходимо мне создать маленькое дочернее popup окошко, чтобы была возможность его и передвигать и текст менять.
Выбрал я для начала класс - "STATIC"
Создаю окно (код на VB, но суть от этого не менятся):
vchWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "STATIC", "привет", WS_POPUP Or WS_CHILD Or WS_BORDER, 0, 0, 50, 20, hWndApp, 0, hInsApp, ByVal 0&)
ShowWindow vchWnd, SW_NORMAL
полученное окошко нормально отображается/прячется, НО я не могу его двигать через MOVEWINDOW. Почитав все что смог найти про класс STATIC я так и не понял, запрещает ли он изменение своего положение и размеров или нет.
Тогда я создал собственный класс:
Dim wndcls As WNDCLASS
wndcls.style = CS_HREDRAW + CS_VREDRAW
wndcls.lpfnwndproc = GetWindowLong(hWndApp, GWL_WNDPROC)
wndcls.cbClsextra = 0
wndcls.cbWndExtra2 = 0
wndcls.hInstance = GetWindowLong(hWndApp, GWL_HINSTANCE)
wndcls.hIcon = 0
wndcls.hCursor = 0
wndcls.hbrBackground = COLOR_WINDOW
wndcls.lpszMenuName = 0
wndcls.lpszClassName = "MyWinPopup"
vchWnd = RegisterClass(wndcls)
vchWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "MyWinPopup", "привет", WS_POPUP Or WS_CHILD Or WS_BORDER, 0, 0, 50, 20, hWndApp, 0, hInsApp, ByVal 0&)
Полученное окно появляется БЕЗ текста и ТОЖЕ не хочет реагировать на MoveWindow (да еще и вылетает потом при закрытии окна).
Вообщем я запутался, в чем может быть трабл??? почему такие нехорошие окна получаются, что никак не хотят двигаться, дело может в комбинации WS_POPUP Or WS_CHILD и WS_EX_TOOLWINDOW?
RTFM!
WS_POPUP Creates a pop-up window. This style cannot be used with the WS_CHILD style.
WS_CHILD Creates a child window. A window with this style cannot have a menu bar. This style cannot be used with the WS_POPUP style.
Насколько я знаю, ни одно окно стандартных классов не запрещает перемещение/изменение размеров.
Посмотри какую ошибку выдает MoveWindow (при помощи GetLastError).
С собственным классом ты действовал вообще неправильно, так что ничего удивительного, что ничего не вышло.
Действительно ошибка была в MoveWindow, свойство hWnd моего класса по ошибке возвращало не число, а тип Boolean. Просто эта строчка с MoveWindow была в другом модуле, а в модуле класса я естественно использовал private переменную и даже не догадывался, что у меня по ошибке свойство Get берет мою private числовую переменную, приравнивает Boolean и возращает вместо числа hWnd мне значение True. Вот значит с утра день рождения начальника отмечать. Мдаа, вот значит неявное преобразование типов.
На счет Child и Popup, понял и оставил токо Popup.
Теперь правда меня мучает другой вопрос:
Хочу дать пользователям класса возможность убирать border у окна динамически. Проблема когда у окна border был, а потом его убрали.
1. Не получается заставить его перерисовать. Все примеры с этими InvalidateRect, UpdateWindow как-то в лучшем случае расчитывают на перерисовку клиентской части окна, а border как я уже убедился к нему не относится и категорически не хочет пропадать. Токо если сверху окно сверху положить и убрать.
2. Когда же border пропадает, возникает другая проблема. Родительское приложение не хочет перерисовывать свое окно, которое находилось под пропавшей рамкой. Т.е. за этот тонкий прямоугольник получается никто уже не отвечает, никому он не нужен и там легко отстаются пиксели чужого окна бывшего некоторое время назад сверху. Что это за несогласованность? или мое окно как-то неправильно убирает рамку?
' получаем текущий стиль окна
lStyle = GetWindowLong(vchWnd, GWL_STYLE)
' устанав. нужный бит
If bolBorder Then
lStyle = lStyle Or WS_BORDER
Else
lStyle = lStyle And (Not WS_BORDER)
End If
' устанвл. новый стиль
lStyle = SetWindowLong(vchWnd, GWL_STYLE, lStyle)
' обновляем окно
Call InvalidateRect(vchWnd, ByVal 0&, API_TRUE)
Call UpdateWindow(vchWnd)
Единственно что помогло это тупое MoveWindow с изменением размера окна хотя бы на один пиксель (причем все равно, ширину или высоту). Тогда все тип-топ, окна разбираются у кого какие границы и не теряют своих пикселей.
Но все таки интересно, почему так происходит, это глюк реализации API в работе с окном у win98?
С помощью 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 передается какой-то конкретный регион.
Думаю собака здесь порылась, но я пока не понимаю откуда его брать.
Поэтому вопрос у меня такой: все ли правильно моих расуждениях и действительно ли на указать регион в WM_NCPAINT для нормальной перерисовки и что это за регион такой?
Цитата:
Originally posted by SergeySV
Так, кое с чем я разобрался. Border у нас относится к НЕКЛИЕНТСКОЙ части окна. Когда я меняю стиль окна и убираю border мне надо заставить окно перерисовать неклиентскую часть окна.
С помощью 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 передается какой-то конкретный регион.
Думаю собака здесь порылась, но я пока не понимаю откуда его брать.
Поэтому вопрос у меня такой: все ли правильно моих расуждениях и действительно ли на указать регион в WM_NCPAINT для нормальной перерисовки и что это за регион такой?
Так, кое с чем я разобрался. Border у нас относится к НЕКЛИЕНТСКОЙ части окна. Когда я меняю стиль окна и убираю border мне надо заставить окно перерисовать неклиентскую часть окна.
С помощью 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 передается какой-то конкретный регион.
Думаю собака здесь порылась, но я пока не понимаю откуда его брать.
Поэтому вопрос у меня такой: все ли правильно моих расуждениях и действительно ли на указать регион в WM_NCPAINT для нормальной перерисовки и что это за регион такой?
какие-то страшные заморочки... наверное это как-то все проще делается, но нет времени проверять.
Насчет регионов - попробуй создать один с помощью CreateRectRgn с координатами окна
"Биты стиля обычно отражают текущее состояние окна, но изменение стиля при помощи функции SetWindowLong не приводит к соответствующему изменпнию окна (по крайней мере, не сразу). Некоторые биты стиля могут успешно изменяться во время работы программы, однако изменение большинства битов правльно действует лишь при создании окна... ...В документации Microsoft не сказано, какие биты стиля могут изменяться во время работы программы; следовательно, вы должны изменять их на свой страх и риск и только после тщательных экспериментов". (С) Дан Эпплман
Но судя по моим экспериментам, изменение рамки у окна возможно, по крайне мере через MoveWindow это получается сделать корректно. Поэтому моя задача сводится просто к нахождению более приемливого решения. - Можно конечно использовать двойной вызов MoveWindow, но хочется с оптимизировать алгоритм. По ходу MoveWindow вызывается много лишних событий, тогда как решающее для моей задачи является только одно. Задача за малым, повторить его... :( :)
Я тут накатал примерчик для показа всего этого безобразия. Это mdb-файл Access. Там на форме выведены кнопки для управления этим Popup окном и наглядной демонстрации.
Я пропустил главное событие - WM_NCCALCSIZE, неверно прочитав в MSDN что в нем предается информация по перерисовки клиенткой области, я решил что это мне не нужно.... а зря, именно в этом весь корень.
В сообщение передается указатель на структуру в которой: размеры окна старого/нового и размеры клиентсокй области окна (старого). Задав размеры клиентской области, я тем самым сообщаю виндам, и размеры неклиентской области. Поэтому когда я меняю стиль окна и убираю рамку, надо сообщить виндам принудительно что размеры неклиентской области = 0.
Уже целенаправленно лазая по инету я нашел сайт где один товарищ, вместо зполнения длинющей структуры и посылки сообщения WM_NCCALCSIZE использовал другую вспомогательную функцию, которая сама формировала такое же сообщение:
SetWindowPos vchWnd, _
0&, 0&, 0&, 0&, 0&, _
SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOZORDER Or _
SWP_NOACTIVATE Or SWP_NOOWNERZORDER Or SWP_FRAMECHANGED
Т.о. мы говорим виндам, что окно не поменяло: своего местоположения,размеров, положение по Z, активности, а изменился только frame и он требует перерисовки.
Вообще разбираясь с неклиентской области я узнал для себя много нового:
С помощью сообщения NCCALCSIZE можно отобрать у окна часть клиентской области под свои нужды. Оказывается есть примеры модулей VB, которые подключатся к станд. TextBox, изменяют клиентскую область и берут перерисовку в неклиентской области на себя, например нумеруют строки TextBox. Да и просто таким образом можно отодвинуть текст от рамки окна, а то он вплотную по умол. идет.