Теперь вы умеете выводить в окно текст, и хотя пока вы пользовались только одним шрифтом, окна наших приложений не останутся пустыми. В этой главе вы познакомитесь с различными типами окон, которые может создать приложение Windows. Обычно ни одно приложение не ограничивается созданием главного окна приложения. Как правило, внутри главного окна создаются другие окна.
Напомним, что для создания окна вам необходимо зарегистрировать класс окна. Есть классы окон, зарегистрированные при инициализации Windows. Ваше приложение может создавать окна либо на базе собственных классов (созданных и зарегистрированных приложением), либо на базе готовых классов, созданных и зарегистрированных самой операционной системой Windows. В этой главе мы расскажем вам только об окнах, созданных приложениями на базе своих собственных классов окон.
На базе одного класса окна приложение может создать несколько окон. Все эти окна могут быть сделаны в одном или нескольких стилях. Стиль окна определяет внешний вид окна и его поведение. Для класса окна также определяется понятие стиля - стиль класса определяет внешний вид и поведение всех окон, созданных на базе данного класса.
Стиль класса окна определяется при регистрации класса окна. Во всех предыдущих примерах приложений мы не задавали стиль окна, определяя содержимое соответствующего поля в структуре WNDCLASS следующим образом:
wc.style = 0;
Стиль класса окна задается в виде отдельных битов, для которых в файле windows.h определены символические константы с префиксом CS_:
Стиль |
Описание |
CS_BYTEALIGNCLIENT |
Внутренняя область окна (client area) должна
быть выравнена по границе байта видеопамяти.
Иногда используется для ускорения процесса
вывода изображения |
CS_BYTEALIGNWINDOW |
Все окно (не только внутренняя область
окна) должно быть выравнено по границе байта
видеопамяти |
CS_CLASSDC |
Необходимо создать единый контекст
отображения, который будет использоваться всеми
окнами, создаваемыми на базе данного класса |
CS_DBLCLKS |
Функция окна будет получать сообщения
при двойном щелчке клавишей мыши (double click) |
CS_GLOBALCLASS |
Данный класс является глобальным и
доступным другим приложениям. Другие приложения
могут создавать окна на базе этого класса |
CS_HREDRAW |
Внутренняя область окна должна быть
перерисована при изменении ширины окна |
CS_NOCLOSE |
В системном меню окна необходимо
запретить выбор функции закрытия окна (строка Close
будет отображаться серым цветом, и ее нельзя
выбрать) |
CS_OWNDC |
Для каждого окна, определяемого на базе
данного класса, будет создаваться отдельный
контекст отображения |
CS_PARENTDC |
Окно будет пользоваться родительским
контекстом отображения, а не своим собственным.
Родительский контекст - это контекст окна,
создавшего другое окно (см. дальше) |
CS_SAVEBITS |
Для данного окна Windows должна сохранять
изображение в виде битового образа (bitmap). Если
такое окно будет перекрыто другим окном, то после
уничтожения перекрывшего окна изображение
первого окна будет восстановлено Windows на
основании сохраненного ранее образа |
CS_VREDRAW |
Внутренняя область окна должна быть
перерисована при изменении высоты окна |
Чаще всего используются стили CS_HREDRAW и CS_VREDRAW:
wc.style = CS_HREDRAW | CS_VREDRAW;
Если для класса заданы стили CS_HREDRAW и CS_VREDRAW, при изменении размеров окна функция окна может получить сообщение WM_PAINT. В этом случае функция окна должна перерисовать часть окна или все окно. Разумеется, если вы просто уменьшили размер окна, перерисовывать ничего не надо, и функция окна в этом случае не получит сообщения WM_PAINT.
Стиль CS_DBLCLKS используется при необходимости отслеживать двойные щелчки мышью. При этом в функцию окна посылаются сообщения WM_LBUTTONDBLCLK и WM_RBUTTONDBLCLK. Если этот стиль не будет задан, как бы быстро вы ни щелкали мышью, функция окна получит только идущие парами сообщения о том, что вы нажимаете и отпускаете левую или правую клавишу мыши.
Остальные приведенные выше классы окна используются реже. Мы будем рассказывать о них по мере необходимости.
Определенный в классе окна стиль класса окна используется при создании на базе этого класса всех окон. Для дальнейшего уточнения внешнего вида и поведения окна используется другая характеристика - стиль окна. Стиль окна указывается при создании окна функцией CreateWindow. В наших примерах основное окно приложения не имело стиля класса окна, но для него был определен стиль окна WS_OVERLAPPEDWINDOW:
hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры
Для определения стиля окна используются символические константы с префиксом WS_, определенные в файле windows.h. С помощью этих констант можно определить примерно два десятка стилей окна, однако чаще всего используются несколько основных стилей.
Мы рассмотрим три основных стиля окон - перекрывающиеся окна (overlapped window), временные окна (pop-up window) и дочерние окна (child window).
Перекрывающиеся окна обычно используются в качестве главного окна приложения. Такие окна имеют заголовок (title bar), рамку и, разумеется, внутреннюю часть окна (client region). Дополнительно перекрывающиеся окна могут иметь (а могут и не иметь) системное меню, кнопки для максимального увеличения размера окна и для сворачивания окна в пиктограмму, вертикальную и горизонтальную полосу просмотра (scroll bar) и меню.
В первых версиях операционной системы Windows (версии 1.х) окна располагались рядом и назывались tiled window (tile - черепица). Сейчас такие окна не используются, вместо них появились перекрывающиеся окна, способные перекрывать окна других приложений. Перекрывающиеся окна называются также окнами верхнего уровня (top-level window).
Файл windows.h содержит следующее определение стиля перекрывающегося окна:
#define WS_OVERLAPPED 0x00000000L
В нашем приложении для определения стиля перекрывающегося окна мы использовали символическую константу WS_OVERLAPPEDWINDOW, определенную как логическое ИЛИ нескольких констант:
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | \ WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX)
Константа WS_OVERLAPPED определяет базовый стиль окна - перекрывающееся окно. Стиль WS_OVERLAPPEDWINDOW в добавление к базовому указывает, что окно должно иметь заголовок (константа WS_CAPTION), системное меню (WS_SYSMENU), толстую рамку для изменения размера окна (WS_THICKFRAME), кнопку минимизации размера окна (WS_MINIMIZEBOX) и кнопку для максимального увеличения размера окна (WS_MAXIMIZEBOX). Если окно имеет заголовок, вы можете его перемещать по экрану.
Вы можете попробовать в предыдущем примере изменить стиль окна, создав свой собственный с использованием приведенных выше констант.
Приложение Windows может создавать несколько окон, связанных между собой "узами родства" и "отношениями собственности". В частности, при создании перекрывающегося окна при помощи функции CreateWindow в качестве восьмого параметра функции вы можете указать так называемый идентификатор окна-владельца. Окно-владелец уже должно существовать на момент создания второго окна, имеющего владельца.
Таким образом, если вы создаете несколько перекрывающихся окон, одни окна могут принадлежать другим.
Если окно-хозяин сворачивается в пиктограмму, все окна, которыми оно владеет, становятся невидимыми. Если вы сначала свернули в пиктограмму окно, которым владеет другое окно, а затем и окно-хозяин, пиктограмма первого (подчиненного) окна исчезает.
Если вы уничтожили окно, автоматически уничтожаются и все принадлежащие ему окна.
Обычное перекрывающееся окно, не имеющее окна-владельца, может располагаться в любом месте экрана и принимать любые размеры. Подчиненные окна располагаются всегда над поверхностью окна-владельца, загораживая его.
Координаты создаваемых функцией CreateWindow перекрывающихся окон указываются по отношению ко всему экрану. Таким образом, если вы создаете перекрывающееся окно с координатами (0, 0), оно будет расположено в верхнем левом углу экрана.
Другим базовым стилем является стиль временных окон, которые обычно используются для вывода информационных сообщений и остаются на экране непродолжительное время.
Временные окна имеют стиль WS_POPUP, определенный в файле windows.h следующим образом:
#define WS_POPUP 0x80000000L
Временные окна, в отличие от перекрывающихся, могут не иметь заголовок (title bar). Если для временного окна определен заголовок, оно может иметь и системное меню. Часто для создания временных окон, имеющих рамку, используется стиль WS_POPUPWINDOW, определенный в файле windows.h следующим образом:
#define WS_POPUPWINDOW (WS_POPUP | WS_BORDER | WS_SYSMENU)
Если надо добавить к временному окну системное меню и заголовок, стиль WS_POPUPWINDOW следует использовать в комбинации со стилем WS_CAPTION, добавляющим заголовок.
Временные окна могут иметь окно владельца и могут сами владеть другими окнами. Все замечания, сделанные нами относительно владения перекрывающимися окнами, справедливы и для временных окон.
Начало системы координат, используемой при создании временных окон, находится в левом верхнем углу экрана. Поэтому при создании временных окон используются экранные координаты (так же, как и при создании перекрывающихся окон).
При изменении размеров временного окна (так же, как и дочернего) функция окна получает сообщение WM_PAINT, в параметрах которого указаны новые размеры окна.
В общем случае вы можете рассматривать временные окна как специальный вид перекрывающихся окон.
Дочерние окна чаще всего используются приложениями Windows. Эти окна нужны для создания органов управления, например таких, как кнопки или переключатели. Windows имеет множество классов, на базе которых созданы стандартные органы управления - кнопки, полосы просмотра и т. п. Все эти органы управления представляют собой дочерние окна. Позже вы будете ими активно пользоваться.
Базовый стиль дочерних окон определяется при помощи константы WS_CHILD:
#define WS_CHILD 0x40000000L
По аналогии с другими базовыми стилями в файле windows.h определена константа WS_CHILDWINDOW, которая полностью эквивалентна константе WS_CHLD:
#define WS_CHILDWINDOW (WS_CHILD)
В отличие от перекрывающихся и временных окон дочерние окна, как правило, не имеют рамки, заголовка, кнопок минимизации и максимального увеличения размера окна, а также полос просмотра. Дочерние окна сами рисуют все, что в них должно быть изображено.
Перечислим особенности дочерних окон.
Само собой разумеется, что дочерние окна должны иметь окно-родителя (окон-сирот не бывает!). Только дочерние окна могут иметь родителей, перекрывающие и временные окна могут иметь окно-хозяина, но не родителя. У дочерних окон могут быть "братья" (или "сестры", кому что больше нравится).
Дочерние окна всегда располагаются на поверхности окна-родителя. При создании дочернего окна начало системы координат расположено в левом верхнем углу внутренней поверхности окна-родителя (но не в верхнем углу экрана, как это было для перекрывающихся и временных окон).
Так как дочерние окна перекрывают окно-родителя, если вы сделаете щелчок мышью над поверхностью дочернего окна, сообщение от мыши попадет в функцию дочернего, но не родительского окна.
При создании дочернего окна в качестве девятого параметра (вместо идентификатора меню, которого не может быть у дочернего окна) функции CreateWindow необходимо указать созданный вами идентификатор дочернего окна. Таким образом, если для какого-либо окна приложения вы создаете несколько дочерних окон, необходимо для каждого окна указать свой идентификатор (типа int). Этот идентификатор будет использован дочерним окном при отправлении сообщений родительскому окну, поэтому вы должны при создании разных дочерних окон использовать разные идентификаторы, хотя это и не обязательно.
Дочернее окно как бы "прилипает" к поверхности родительского окна и перемещается вместе с ним. Оно никогда не может выйти за пределы родительского окна. Все дочерние окна скрываются при сворачивании окна-родителя в пиктограмму и появляются вновь при восстановлении родительского окна.
При изменении размеров родительского окна дочерние окна, на которых отразилось такое изменение (которые вышли за границу окна и появились вновь), получают сообщение WM_PAINT. При изменении размеров родительского окна дочерние окна не получают сообщение WM_SIZE. Это сообщение попадает только в родительское окно.
Приведем полный список возможных стилей окна, определенных в виде символических констант в файле windows.h. Некоторые из приведенных здесь стилей будут подробно рассмотрены в соответствующих главах этого тома или в следующих томах "Библиотеки системного программиста".
Имя константы |
Описание стиля |
WS_BORDER |
Окно с рамкой |
WS_CAPTION |
Окно с заголовком. Этот стиль
несовместим со стилем WS_DLGFRAME. При использовании
стиля WS_CAPTION подразумевается использование стиля
WS_BORDER |
WS_CHILD |
Дочернее окно. Несовместим со стилем
WS_POPUP |
WS_CHILDWINDOW |
То же самое, что и WS_CHILD |
WS_CLIPCHILDREN |
Этот стиль используется при создании
родительского окна. При его использовании
родительское окно не перерисовывает свои
внутренние области, занятые дочерними окнами |
WS_CLIPSIBLINGS |
При указании этого стиля дочерние окна
не перерисовывают свои области, перекрытые
"братьями", то есть другими дочерними
окнами, имеющими тех же родителей |
WS_DISABLED |
Вновь созданное окно сразу становится
заблокированным (не получает сообщения от мыши и
клавиатуры) |
WS_DLGFRAME |
Окно с двойной рамкой без заголовка.
Несовместим со стилем WS_CAPTION |
WS_GROUP |
Определяет первый орган управления в
группе органов управления. Используется только в
диалоговых панелях |
WS_HSCROLL |
В окне создается горизонтальная полоса
просмотра |
WS_ICONIC |
То же самое, что и WS_MINIMIZE |
WS_MAXIMIZE |
Создается окно максимально возможного
размера |
WS_MAXIMIZEBOX |
Окно содержит кнопку для увеличения его
размера до максимально возможного. Этот стиль
необходимо использовать вместе со стилями
WS_OVERLAPPED или WS_CAPTION, в противном случае указанная
кнопка не появится |
WS_MINIMIZE |
Создается окно, уменьшенное до предела
(свернутое в пиктограмму). Этот стиль необходимо
использовать вместе со стилем WS_OVERLAPPED |
WS_MINIMIZEBOX |
Окно содержит кнопку для сворачивания
окна в пиктограмму (минимизации размеров окна).
Этот стиль необходимо использовать вместе со
стилем WS_OVERLAPPED или WS_CAPTION, в противном случае
указанная кнопка не появится |
WS_OVERLAPPED |
Создается перекрывающееся окно, имеющее
заголовок и рамку |
WS_OVERLAPPEDWINDOW |
Создается перекрывающееся окно, имеющее
заголовок, рамку для изменения размера окна,
системное меню, кнопки для изменения размеров
окна. Этот стиль является комбинацией стилей
WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX |
WS_POPUP |
Создается временное (pop-up) окно |
WS_POPUPWINDOW |
Комбинация стилей WS_POPUP, WS_BORDER и WS_SYSMENU.
Для того чтобы сделать системное меню доступным,
необходимо дополнительно использовать стиль
WS_CAPTION |
WS_SYSMENU |
Окно должно иметь системное меню |
WS_TABSTOP |
Этот стиль указывает орган управления,
на который можно переключиться при помощи
клавиши <Tab>. Данный стиль может быть
использован только дочерними окнами в
диалоговых панелях |
WS_THICKFRAME |
Окно должно иметь толстую рамку для
изменения размера окна |
WS_VISIBLE |
Создается окно, которое сразу
становится видимым. По умолчанию окна создаются
невидимыми, и для их отображения требуется
вызывать функцию ShowWindow |
WS_VSCROLL |
В окне создается вертикальная полоса
просмотра |
WS_TILED |
Устаревший стиль, аналогичен WS_OVERLAPPED |
WS_SIZEBOX |
Устаревший стиль, аналогичен WS_THICKFRAME |
WS_TILEDWINDOW |
Устаревший стиль, аналогичен WS_OVERLAPPEDWINDOW |
MDIS_ALLCHILDSTYLES |
Этот стиль используется при создании
дочерних MDI-окон и определяет окна, которые могут
иметь любые комбинации стилей. По умолчанию
дочерние MDI-окна имеют стили WS_MINIMIZE, WS_MAXIMIZE, WS_VSCROLL,
WS_HSCROLL |
Приведенные выше стили не всегда совместимы друг с другом. Например, перекрывающееся окно не может быть одновременно еще и временным. Пользуясь приведенной ниже таблицей, вы сможете определить совместимость стилей. В этой таблице символом "+" отмечены стили, которые можно использовать для создания перекрывающихся, временных и дочерних окон.
Имя константы |
Перекрывающееся окно |
Временное окно |
Дочернее окно |
WS_BORDER |
+ |
+ |
+ |
WS_CAPTION |
+ |
+ |
+ |
WS_CHILD |
|
|
+ |
WS_CHILDWINDOW |
|
|
+ |
WS_CLIPCHILDREN |
+ |
+ |
+ |
WS_CLIPSIBLINGS |
|
|
+ |
WS_DISABLED |
+ |
+ |
+ |
WS_DLGFRAME |
+ |
+ |
+ |
WS_GROUP |
|
|
+ |
WS_HSCROLL |
+ |
+ |
+ |
WS_ICONIC |
+ |
|
|
WS_MAXIMIZE |
+ |
|
|
WS_MAXIMIZEBOX |
+ |
+ |
+ |
WS_MINIMIZE |
+ |
|
|
WS_MINIMIZEBOX |
+ |
+ |
+ |
WS_OVERLAPPED |
+ |
|
|
WS_OVERLAPPEDWINDOW |
+ |
|
|
WS_POPUP |
|
+ |
|
WS_POPUPWINDOW |
|
+ |
|
WS_SYSMENU |
+ |
+ |
+ |
WS_TABSTOP |
|
|
+ |
WS_THICKFRAME |
+ |
+ |
+ |
WS_VISIBLE |
+ |
+ |
|
WS_VSCROLL |
+ |
+ |
+ |
WS_TILED |
+ |
|
|
WS_SIZEBOX |
+ |
+ |
+ |
WS_TILEDWINDOW |
+ |
|
|
MDIS_ALLCHILDSTYLES |
|
|
|
В дополнение к перечисленным выше стилям, используемым при создании окон функцией CreateWindow, существуют так называемые расширенные стили окна (extended window styles). Окна с расширенными стилями должны создаваться функцией CreateWindowEx. Эта функция имеет следующий прототип:
HWND CreateWindowEx(DWORD dwExStyle, LPCSTR lpszClassName, LPCSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU hmenu, HINSTANCE hinst, void FAR* lpvCreateParams);
Функции CreateWindowEx в качестве первого параметра (dwExStyle) необходимо указать расширенный стиль окна. Остальные параметры в точности соответствуют параметрам функции CreateWindow.
Приведем список расширенных стилей окна.
Имя константы |
Описание стиля |
WS_EX_ACCEPTFILES |
Окно способно принимать файлы,
перенесенные с использованием технологии drag-drop |
WS_EX_DLGMODALFRAME |
Окно имеет двойную рамку и
дополнительно может иметь стиль WS_CAPTION |
WS_EX_NOPARENTNOTIFY |
Дочернее окно с этим стилем не будет
посылать родительскому окну сообщение WM_PARENTNOTIFY.
Обычно, когда дочернее окно создается или
уничтожается или когда вы щелкаете мышью над
дочерним окном, это сообщение посылается
родительскому окну |
WS_EX_TOPMOST |
Окно будет видно всегда, даже когда оно
заблокировано |
WS_EX_TRANSPARENT |
Этот стиль позволяет создать прозрачное
окно. Оно получает сообщение WM_PAINT только после
того, как все окна-братья получили сообщение WM_PAINT
и обновили свои окна |
Приведем таблицу совместимости расширенных стилей с перекрывающимися, временными и дочерними окнами.
Имя константы |
Перекрывающееся окно |
Временное окно |
Дочернее окно |
WS_EX_ACCEPTFILES |
+ |
+ |
+ |
WS_EX_DLGMODALFRAME |
+ |
+ |
+ |
WS_EX_NOPARENTNOTIFY |
|
|
+ |
WS_EX_TOPMOST |
+ |
+ |
|
WS_EX_TRANSPARENT |
+ |
+ |
+ |
Для того чтобы вы смогли проводить эксперименты со стилями окон и классов, мы подготовили приложение WSTYLE (листинг 3.1). В этом приложении создается одно главное окно, одно перекрывающееся (этим окном владеет главное), одно временное (также принадлежащее главному окну) и одно дочернее окно.
Листинг 3.1. Файл wstyle\wstyle.cpp
// ---------------------------------------- // Демонстрация стилей окна // ---------------------------------------- #define STRICT #include <windows.h> #include <windowsx.h> #include <mem.h> // Прототипы функций BOOL Register(HINSTANCE); LRESULT CALLBACK _export MainWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK _export ChildWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK _export PopUpWndProc(HWND, UINT, WPARAM, LPARAM); // Имя класса для главного окна приложения char const szMainClassName[] = "WStyleAppClass"; // Имя класса для дочерних окон char const szChildClassName[] = "WStyleAppChildClass"; // Имя класса для временных окон char const szPopUpClassName[] = "WStyleAppPopUpClass"; // Заголовок главного окна приложения char const szMainWindowTitle[] = "WStyle Application"; // Заголовок дочернего окна char const szChildWindowTitle[] = "Окно Child"; // Заголовок временного окна char const szPopUpWindowTitle[] = "Окно PopUp"; // ===================================== // Функция WinMain // ===================================== #pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND MainHwnd; // идентификатор главного окна приложения HWND ChildHwnd; // идентификатор дочернего окна HWND PopUpHwnd; // идентификатор временного окна HWND OwnedHwnd; // идентификатор окна, которым владеет // главное окно приложения // Регистрируем классы окон if(!Register(hInstance)) return FALSE; // Создаем главное окно приложения MainHwnd = CreateWindow( szMainClassName, // имя класса окна szMainWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры // Если создать окно не удалось, завершаем приложение if(!MainHwnd) return FALSE; // Делаем окно видимым ShowWindow(MainHwnd, nCmdShow); // Посылаем в окно сообщение WM_PAINT UpdateWindow(MainHwnd); // Создаем перекрывающееся окно, которое // принадлежит главному окну приложения OwnedHwnd = CreateWindow( szMainClassName, // имя класса окна "Перекрывающееся окно", // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна 20, // задаем размеры и расположение 200, // окна 300, 100, MainHwnd, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры // Если создать окно не удалось, завершаем приложение if(!OwnedHwnd) return FALSE; // Делаем окно видимым ShowWindow(OwnedHwnd, nCmdShow); // Посылаем в окно сообщение WM_PAINT UpdateWindow(OwnedHwnd); // Создаем дочернее окно ChildHwnd = CreateWindow( szChildClassName, // имя класса окна "Дочернее окно", // заголовок окна WS_CHILDWINDOW | WS_VISIBLE | WS_CAPTION, // стиль окна 300, // задаем размеры и расположение 20, // окна 200, 100, MainHwnd, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры // Если создать окно не удалось, завершаем приложение if(!ChildHwnd) return FALSE; // Создаем временное окно PopUpHwnd = CreateWindow( szPopUpClassName, // имя класса окна "Временное окно", // заголовок окна WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE, // стиль окна 100, // задаем размеры и расположение 100, // окна 200, 100, MainHwnd, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры // Если создать окно не удалось, завершаем приложение if(!PopUpHwnd) return FALSE; // Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); } return msg.wParam; } // ===================================== // Функция Register // Выполняет регистрацию классов окна, // используемых приложением // ===================================== BOOL Register(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна memset(&wc, 0, sizeof(wc)); // -------------------------------------------------- // Регистрируем класс окна для главного // и перекрывающегося окна // -------------------------------------------------- wc.style = 0; wc.lpfnWndProc = (WNDPROC) MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szMainClassName; aWndClass = RegisterClass(&wc); if(aWndClass == 0) return FALSE; // -------------------------------------------------- // Регистрируем класс окна для // дочернего окна // -------------------------------------------------- wc.lpfnWndProc = (WNDPROC) ChildWndProc; wc.hCursor = LoadCursor(NULL, IDC_SIZE); wc.hbrBackground = GetStockBrush(GRAY_BRUSH); wc.lpszClassName = (LPSTR)szChildClassName; aWndClass = RegisterClass(&wc); if(aWndClass == 0) return FALSE; // -------------------------------------------------- // Регистрируем класс окна для // временного окна // -------------------------------------------------- wc.lpfnWndProc = (WNDPROC) PopUpWndProc; wc.hCursor = LoadCursor(NULL, IDC_CROSS); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = (LPSTR)szPopUpClassName; aWndClass = RegisterClass(&wc); if(aWndClass == 0) return FALSE; return TRUE; } // ===================================== // Функция MainWndProc // Используется главным окном приложения // и созданным дополнительно // перекрывающимся окном // ===================================== LRESULT CALLBACK _export MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования switch (msg) { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 10, 20, "WM_PAINT", 8); EndPaint(hwnd, &ps); return 0; } case WM_LBUTTONDOWN: { hdc = GetDC(hwnd); TextOut(hdc, 10, 40, "WM_LBUTTONDOWN", 14); ReleaseDC(hwnd, hdc); return 0; } case WM_DESTROY: { PostQuitMessage(0); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); } // ===================================== // Функция ChildWndProc // Используется дочерним окном // ===================================== LRESULT CALLBACK _export ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования switch (msg) { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 10, 20, "WM_PAINT", 8); EndPaint(hwnd, &ps); return 0; } case WM_LBUTTONDOWN: { hdc = GetDC(hwnd); TextOut(hdc, 10, 40, "WM_LBUTTONDOWN", 14); ReleaseDC(hwnd, hdc); return 0; } case WM_RBUTTONDOWN: { MessageBeep(-1); // звуковой сигнал return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); } // ===================================== // Функция PopUpWndProc // Используется временным окном // ===================================== LRESULT CALLBACK _export PopUpWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования switch (msg) { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 10, 20, "WM_PAINT", 8); EndPaint(hwnd, &ps); return 0; } case WM_LBUTTONDOWN: { hdc = GetDC(hwnd); TextOut(hdc, 10, 40, "WM_LBUTTONDOWN", 14); ReleaseDC(hwnd, hdc); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); }
Приложение регистрирует три класса окна со следующими именами:
char const szMainClassName[] = "WStyleAppClass"; char const szChildClassName[] = "WStyleAppChildClass"; char const szPopUpClassName[] = "WStyleAppPopUpClass";
Первый класс с именем, записанным в массиве szMainClassName, используется для создания главного окна приложения и одного дополнительного перекрывающегося окна.
Класс с именем, записанным в массиве szChildClassName, используется для создания дочернего окна, а класс с именем, записанным в массиве szPopUpClassName, - для создания временного окна.
Для каждого класса окон в приложении используется отдельная функция окна. Главное окно и дополнительное временное окно работают с функцией MainWndProc, для дочернего окна используется функция окна с именем ChildWndProc, а для временного - PopUpWndProc.
Для каждого окна определен свой заголовок:
char const szMainWindowTitle[] = "WStyle Application"; char const szChildWindowTitle[] = "Окно Child"; char const szPopUpWindowTitle[] = "Окно PopUp";
Так как приложение создает четыре различных окна, в функции WinMain определены четыре переменные, предназначенные для хранения идентификаторов окон. Эти переменные имеют имена MainHwnd, ChildHwnd, PopUpHwnd и OwnedHwnd.
Переменная MainHwnd используется для хранения идентификатора главного окна приложения. Переменные ChildHwnd и PopUpHwnd предназначены соответственно для хранения идентификаторов дочернего и временного окна. В переменной OwnedHwnd хранится идентификатор дополнительного перекрывающегося окна, принадлежащего главному окну приложения.
Выполнение приложения начинается с регистрации классов окон, выполняемых функцией Register, определенной в приложении.
Регистрация класса для главного окна приложения не имеет никаких особенностей по сравнению с аналогичной операцией, выполняемой нашими предыдущими приложениями:
wc.style = 0; wc.lpfnWndProc = (WNDPROC) MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szMainClassName; aWndClass = RegisterClass(&wc); if(aWndClass == 0) return FALSE;
При регистрации класса для дочернего окна для упрощения программы используется проинициализированная ранее структура wc. В этой структуре необходимо заменить указатель на функцию окна (дочернее окно работает с функцией ChildWndProc) и имя класса (поле lpszClassName). Дополнительно для дочернего окна мы изменили форму курсора мыши и цвет фона:
wc.lpfnWndProc = (WNDPROC) ChildWndProc; wc.hCursor = LoadCursor(NULL, IDC_SIZE); wc.hbrBackground = GetStockBrush(GRAY_BRUSH); wc.lpszClassName = (LPSTR)szChildClassName;
Когда вы поместите курсор мыши в дочернее окно, он будет иметь форму четырех стрелок, указывающих в разные стороны. Фон окна будет закрашен в серый цвет.
При регистрации класса временного окна мы указываем адрес функции окна с именем PopUpWndproc, определяем форму курсора в виде перекрестия, используем системный цвет для фона, а также указываем другое имя класса:
wc.lpfnWndProc = (WNDPROC) PopUpWndProc; wc.hCursor = LoadCursor(NULL, IDC_CROSS); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = (LPSTR)szPopUpClassName;
В случае успешной регистрации всех классов функция Register возвращает значение TRUE.
После регистрации классов окна в функции WinMain создаются четыре окна.
Первым создается главное окно приложения:
MainHwnd = CreateWindow( szMainClassName, // имя класса окна szMainWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры
Главное окно приложения представляет собой обычное перекрывающееся окно класса WS_OVERLAPPEDWINDOW. Так как главное окно не может никому принадлежать, для параметра функции CreateWindow, определяющего идентификатор родительского окна или окна-владельца, используется нулевое значение.
Для того чтобы сделать окно видимым и нарисовать его внутреннюю область, вызываются функции ShowWindow и UpdateWindow.
После главного окна создается перекрывающееся окно. Это окно создается на базе того же класса, что и главное окно приложения, поэтому оно, как и главное окно приложения, работает с функцией окна MainWndProc.
Для перекрывающегося окна мы определяем свой заголовок, стиль WS_OVERLAPPEDWINDOW, явно указываем координаты левого верхнего угла окна (20, 200), размеры окна (300, 100), а также (вместо идентификатора родительского окна) идентификатор окна-владельца:
OwnedHwnd = CreateWindow( szMainClassName, // имя класса окна "Перекрывающееся окно", // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна 20, // задаем размеры и расположение 200, // окна 300, 100, MainHwnd, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры
Для того чтобы сделать перекрывающееся окно видимым и нарисовать его внутреннюю область, вызываются функции ShowWindow и UpdateWindow. В качестве параметра им передается идентификатор перекрывающегося окна OwnedHwnd.
Расположение и размеры окна задаются для простоты абсолютными значениями, что не всегда удобно. Через некоторое время вы познакомитесь с функциями программного интерфейса Windows, позволяющими узнать различные метрические характеристики, такие, как размер окна, размер системного шрифта, размеры пиктограммы и т. д. Используя эти функции вы можете определять размеры и расположение окон, исходя из размеров экрана или главного окна приложения.
Далее приложение создает дочернее окно:
ChildHwnd = CreateWindow( szChildClassName, // имя класса окна "Дочернее окно", // заголовок окна WS_CHILDWINDOW | WS_VISIBLE | WS_CAPTION, // стиль окна 300, // задаем размеры и расположение 20, // окна 200, 100, MainHwnd, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры
В качестве стиля окна используется константа WS_CHILDWINDOW, к которой добавлены константы WS_CAPTION и WS_VISIBLE. Константа WS_CAPTION добавляет к дочернему окну заголовок, благодаря чему окно можно перемещать по поверхности экрана. Если указана константа WS_VISIBLE, окно создается сразу видимым, поэтому у нас нет необходимости для отображения дочернего окна вызывать функцию ShowWindow.
Для дочернего окна указан идентификатор родительского окна, в качестве которого выступает главное окно приложения.
Временное окно создается аналогично дочернему:
PopUpHwnd = CreateWindow( szPopUpClassName, // имя класса окна "Временное окно", // заголовок окна WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE, // стиль окна 100, // задаем размеры и расположение 100, // окна 200, 100, MainHwnd, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные // параметры
Для стиля временного окна используется комбинация констант WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE. При этом создается временное окно, которое имеет строку заголовка и становится видимым сразу после его создания функцией CreateWindow.
После создания всех окон запускается цикл обработки сообщений.
Окна приложения WSTYLE показаны на рис. 3.1.
Рис. 3.1. Окна приложения WSTYLE
Теперь займемся функциями окна, определенными в приложении.
Функция MainWndProc обрабатывает сообщения WM_PAINT, WM_LBUTTONDOWN и WM_DESTROY.
По сообщению WM_PAINT в окно выводится строка "WM_PAINT". Если в функцию окна приходит сообщение WM_LBUTTONDOWN, в окно выводится строка "WM_LBUTTONDOWN". Заметьте, что строка "WM_LBUTTONDOWN" выводится в то окно, в котором вы нажали на левую клавишу мыши. Если в функцию окна приходит сообщение WM_DESTROY, вызывается функция PostQuitMessage и приложение завершает свою работу.
Функция окна ChildWndProc используется для обработки сообщений, поступающих в дочернее окно. Эта функция также обрабатывает сообщения WM_PAINT и WM_LBUTTONDOWN, выводя соответствующие строки в дочернее окно. Дополнительно функция дочернего окна обрабатывает сообщение WM_RBUTTONDOWN. Если приходит это сообщение, вызывается функция MessageBeep, которая выдает звуковой сигнал.
Функция временного окна PopUpWndProc аналогична функции ChildWndProc, но не обрабатывает сообщение WM_RBUTTONDOWN.
Таким образом, два окна приложения (главное и перекрывающееся) используют одну общую функцию окна MainWndProc. Дочернее окно и временное окно используют свои собственные функции окна.
Несмотря на то что главное окно приложения и созданное дополнительно перекрывающееся окно используют общую функцию окна, когда вы нажимаете в том или другом окне на левую клавишу мыши, строка "WM_LBUTTONDOWN" выводится именно в том окне, где была нажата клавиша. Так и должно быть, ведь в функцию окна передается идентификатор окна, который используется для вывода текста. Главное и перекрывающееся окна используют разные идентификаторы, поэтому общая для этих окон функция окна выводит текст в нужное окно.
Файл определения модуля, использованный приложением WSTYLE, приведен в листинге 3.2.
Листинг 3.2. Файл wstyle\wstyle.def
; ============================= ; Файл определения модуля ; ============================= NAME WSTYLE DESCRIPTION 'Приложение WSTYLE, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple