В предыдущей главе мы рассмотрели терминологию и основные концепции программирования в среде Windows. Наиболее важными и привлекательными особенностями всех приложений Windows следует считать стандартность интерфейса, аппаратную независимость и возможность одновременного выполнения. В этой главе мы, воспользовавшись полученными знаниями, попытаемся написать несложное процедурное приложение.
Структура приложения
В данном параграфе мы рассмотрим компоненты, из которых состоит программа SWP.C (SimpleWindowsProgram). Будет показано, что необходимо включить в программу для создания и отображения на экране окна приложения (с рамкой, строкой заголовка, системным меню и кнопками свертывания/развертывания), а также для корректного завершения приложения.
Вы также узнаете, как программу SWP.Cи связанные с ней файлы можно использовать в качестве шаблона при создании других приложений Windows. Четкое понимание структуры нашего примера позволит вам сэкономить много времени в будущем, так как рассматриваемые компоненты составляют основу практически любого приложения.
Но для начала в качестве справки перечислим наиболее часто используемые типы данных Windows, которые могут встретиться нам в процессе работы (табл. 17.1).
Таблица 17.1. Часто используемые типы данных | |
Тип | Описание |
callback | Замещает спецификацию farpascal в функциях обратного вызова |
handle | 32-разрядное беззнаковое целое число, используемое в качестве дескриптора |
HDC |
Дескриптор контекста устройства |
hwnd | Дескриптор окна |
LONG | 32-разрядное целое число со знаком |
lparam | Используется для описания младшего аргумента сообщения |
LPCSTR | То же, что и lpstr, но используется для указания константных строк |
lpstr |
32-разрядный указатель на строку |
lpvoid | Обобщенный тип указателя, эквивалентен void* |
lresult | Используется для возвращения значений из оконных процедур |
uint |
32-разрядное беззнаковое целое число |
wchar | 16-разрядный символ UNICODE; используется для представления печатных символов всех языков мира |
winapi | Замещает спецификацию farpascal в описаниях API-функций |
wparam | Используется для описания старшего параметра сообщения |
В табл. 17.2 приведено несколько стандартных структур Windows.
Таблица 17.2. Часто используемые стандартные структуры | |
Структура | Что определяет |
msg |
Параметры сообщения |
PAINTSTRUCT |
Область окна, требующую перерисовки |
rect | Прямоугольную область |
WNDCLASS | Класс окна |
Основные компоненты приложения
Приложения Windowsсодержат два общих элемента: функцию winMain() и оконную процедуру. Функция winMain() составляет основу любого приложения. Она служит как бы точкой входа в приложение и выполняет ту же роль, что и функция main() в обычных программах на С и C++.
Оконная процедура выполняет роль диспетчера сообщений. Приложения никогда не получают к ней прямого доступа. Для этого им сначала нужно сделать запрос к Windows на выполнение соответствующего действия. Поэтому все оконные процедуры должны быть функциями обратного вызова. Подобная функция регистрируется в Windows и вызывается всякий раз, когда выполняется некоторое действие над окном.
Функция winMain(), как уже было сказано, является обязательным компонентом любого приложения Windows. С нее начинается выполнение программы и ею же обычно заканчивается. Функция WinMain() отвечает за следующее:
В функцию WinMain() передаются четыре параметра:
int WINAPI WinMain(HINSTANCE
hlnst, HINSTANCE
hPrelnst, LPSTR
IpszCmdLine, int nCmdShow)
Первый параметр, hlnst, представляет собой дескриптор экземпляра приложения. Второй, hPrelnst, всегда содержит значение null, что указывает на отсутствие других, запущенных ранее, экземпляров приложения.
Примечание
В ранних версиях Windows(вплоть до 3.x) параметр hPrelnst позволял определить, были ли запущены другие копии приложения. В Windows 95/98/NTдля каждого запущенного модуля выделяется отдельное адресное пространство, в связи с чем параметр hPrelnstвсегда устанавливается равным NULL.
Третий параметр, IpszCmdLine, служит указателем на строку, завершающуюся нулевым символом и содержащую командную строку программы.
Последний параметр, nCmdShow, задает одну из многочисленных констант, определяющих возможные варианты отображения окна, например SW_SHOWNORMAL, SWSHOWMAXIMIZEDИЛИ SWSHOWMINIMIZED.
Структура WNDCLASS
Одна из задач функции WinMain() заключается в регистрации класса окна приложения. Класс окна содержит комбинацию выбранных пользователем свойств окна, дескрипторы значка и указателя мыши, а также прочие атрибуты и выступает в роли шаблона при создании всех экземпляров окон приложения.
Примечание
В ранних версиях Windows зарегистрированные классы окон становились доступными для всех программ, выполняющихся в системе. В связи с этим программисты должны были при регистрации классов следить за тем, чтобы используемые ими имена не конфликтовали с другими зарегистрированными классами окон. В Windows 95/98/NTтребуется, чтобы не только приложения, но и все экземпляры одного приложения регистрировали собственные классы окон.
Для описания всех классов окон используется единая структура.
Следующий пример взят непосредственно из файла WINUSER.H, включаемого в файл
WINDOWS.H с помощью директивы #include.
typedef struct tagWNDCLASSW (
UINT style
WNDPROC IpfnWndProc
int cbClsExtra;
int cbWndExtra;
HANDLE hlnstance;
HICON hlcon;
HCURSOR hCursor
HBRUSH hbrBackground;
LPCWSTR IpszMenuName;
LPCWSTR IpszClass'Name;
} WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
Структура wndclassw предназначена для работы с UNICODE-строками. Имеется также аналогичная ей структура wndclassa, ориентированная на работу с ASCII-строками. С помощью typedef-определений от этих структур порождается универсальная Структура wndclass, позволяющая программисту не заботиться о формате используемых строк.
Чтобы определить класс окна, приложение должно объявить переменную следующего типа:
WNDCLASS wcApp;
Впоследствии переменная wcApp заполняется информацией о классе окна. Ниже описываются различные поля структуры wndclass.
style
Определяет свойства окна. С помощью операции побитового ИЛИ можно задать комбинацию из нескольких свойств. Возможные свойства поля style перечислены в табл. 17.3.
Свойство | Описание |
CS_BYTEALIGNCLIENT | Задает выравнивание по границе байта размера рабочей области по горизонтали |
CS_BYTEALIGNWINDOW | Задает выравнивание по границе байта размера окна по горизонтали |
CS_CLASSDC | Указывает, что все окна данного класса должны использовать один и тот же контекст устройства |
CS_DBLCLKS | При установке этого флага окну будут посылаться сообщения о двойном щелчке |
CS_GLOBALCLASS | Указывает, что класс окна является глобальным и должен регистрироваться независимо от значения параметра hlnstance |
CS_HREDRAW | Задает операцию перерисовки окна при изменении горизонтального размера |
CS_NOCLOSE | Делает неактивной команду close в системном меню |
CS_ОWNDC | Задает вьщеление каждому окну собственного контекста устройства |
CS_PARENTDC | Указывает, что область отсечения дочернего окна будет равна области отсечения родительского окна |
CS_SAVEBITS |
Служит указанием системе сохранять часть окна, перекрываемую другим окном, чтобы впоследствии не посылать перекрытому окну сообщения WM_PAINT, а восстанавливать его содержимое самостоятельно |
CS_VREDRAW | Задает операцию перерисовки окна при изменении вертикального размера |
Содержит указатель на оконную процедуру, которая будет обрабатывать все сообщения, посылаемые окну.
cbClsExtra
Содержит информацию о количестве байтов, выделяемых системой после создания структуры класса окна. Значение этого параметра может быть равным NULL.
cbWndExtra
Содержит информацию о количестве байтов, выделяемых системой после создания экземпляра окна. Этот параметр может быть равным NULL.
hlnstance
Содержит дескриптор экземпляра приложения, регистрирующего класс окна.
hlcon
Содержит дескриптор значка приложения. Этот параметр может быть равным
NULL.
hCursor
Содержит дескриптор указателя мыши, используемого в окне приложения. Этот параметр может быть равным null.
hbrBackground
Определяет кисть, используемую для заливки фона окна. Может содержать дескриптор реальной кисти или одну из следующих констант (к задаваемой константе следует прибавить 1):
COLOR_ACTIVEBORDER
COLOR_ACTIVECAPTION
COLOR_APPWORKSPACE
COLOR_BACKGROUND
COLOR_BTNSHADOW
COLORJBTNTEXT
COLOR_CAPTIONTEXT
CQLORJ3RAYTEXT
COLOR_HIGHLIGHT
COLOR_HI GHLIGHTTEXT
CQLOR_INACTIVEBORDER
COLOR_INACTIVECAPTION
COLOR_MENU
COLOR_MENUTEXT
COLOR_SCROLLBAR
COLOR_WINDOW
COLOR_WINDOWFRAME
Если значение параметра hbrBackground равно NULL, то приложение должно самостоятельно управлять заливкой фона окна при получении сообщения
WM_ERASEBKGND.
lpszMemiName
Содержит указатель на строку, завершающуюся нулевым символом и представляющую собой имя ресурса меню. Этот параметр может быть равным null.
lpszClassName
Содержит указатель на строку, завершающуюся нулевым символом и представляющую собой имя класса окна.
Структура WNDCIASSEX
Существует расширенный вариант структуры wndclass, получивший название wndclassex. От wndclassон отличается тем, что дополнительно позволяет использовать в приложении значок малого размера.
typedef struct _WNDCLASSEX {
UINT cbSize
UINT style;
WNDPROC IpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLEhlnstance;
HICON hlcon;
HCURSOR hCursor
HBRUSHhbrBackground;
LPCTSTR IpszMenuName;
LPCTSTR IpszClassName;
HICON hlconSm
) WNDCLASSEX;
В поле cbSize задается размер всей структуры. Это обычно делается с помощью выражения sizeof(WNDCLASSEX). В поле hlconSm содержится дескриптор малого значка.
В приложении можно Описать собственный класс окна, создав для него структуру соответствующего типа и заполнив поля структуры атрибутами разрабатываемого приложения.
Следующий фрагмент взят из файла SWP.C и демонстрирует, как можно создать и проинициализировать структуру wndclass:
char szProgName[]="ProgName";
WNDCLASS wcApp;
wcApp.lpszClassName = szProgName;
wcApp.hlnstance = hlnst;
wcApp.lpfnWndProc = WndProc;
wcApp.hCursor
= LoadCursor(NULL, IDC_ARROW);
wcApp.hlcon = NULL; wcApp.lpszMenuName
= 0;
wcApp.hbrBackground = GetStockObject(WHITE_BRUSH);
wcApp. style = CS_HREDRAW
| CS_VREDRAW;
wcApp.cbClsExtra = 0;
wcApp.cbWndExtra = 0; if (IRegisterClass(SwcApp))
return 0;
Имя программы хранится в переменной szProgNameи записывается в класс окна в поле wcApp.IpszClassName.
Второму полю структуры wndclass, называемому wcApp. hinstance, передается аргумент hlnstфункции winMain(), хранящий дескриптор текущего экземпляра приложения. Полю ipfnWndProc присваивается указатель на оконную процедуру, которая будет обрабатывать все сообщения, посылаемые окну. В файле SWP.C эта функция называется WndProc().
Имя WndProc() задано программистом, т.е. не является стандартным. Прототип функции должен быть описан до выполнения операции присваивания.
В поле wcApp.hCursor хранится дескриптор указателя мыши текущего экземпляра приложения. В нашем примере создается указатель idc_arrow(соответствует обычному указателю мыши в виде наклоненной стрелки), дескриптор которого возвращается функцией LoadCursor(). Поскольку в приложении нет устанавливаемого по умолчанию значка, полю wcApp.hlcon присваивается значение null.
Если поле wcApp.lpszMenuName содержит NULL, то Windows считает, что у приложения нет собственного меню. В противном случае должно быть указано название ресурса меню, взятое в кавычки. Функция GetStockObject() возвращает дескриптор кисти, которая используется для заливки фона окна, созданного на базе данного класса. В нашем случае применяется стандартная белая кисть — WHITEBRUSH.
В поле wcApp. style свойства окна задаются как cs_HREDRAW в сочетании с cs_vredraw. Константы с префиксом cs_ определены в файле WINUSER.H и могут объединяться с помощью операции побитового ИЛИ (|). Константы cs_hredraw и cs_vredraw указывают Windows, что рабочую область окна нужно перерисовывать всякий раз, когда размер окна изменяется по вертикали или горизонтали.
Последние два поля, wcApp.cbClsExtra и wcApp.cbWndExtra, обычно устанавливают равными 0. Их предназначение — указание дополнительного количества байтов памяти, которые должны быть зарезервированы после создания класса окна или экземпляра самого окна.
Ранее мы уже говорили о том, что в 16-разрядных версиях Windows приложение регистрировало класс окна только в том случае, если не существовало ранее загруженных копий этой же программы. Зарегистрированный класс окна становился доступным для всех остальных программ, поэтому необходимость в повторной его регистрации не возникала. Вот как это делалось:
if (IhPrelnst)
{
if (IRegisterClass(SwcApp))
return FALSE; }
В 32-разрядных версиях Windows параметр hPrelnst всегда равен NULL, поэтому подобная проверка избыточна.
В функцию Register-Class() передается указатель на только что созданную структуру класса окна. Если Windows не может зарегистрировать новый класс (скажем, в случае нехватки памяти), функция возвращает 0, вследствие чего выполнение программы завершается.
Создание окна
Регистрация класса окна еще не свидетельствует о создании самого окна. Окно создается в результате вызова функции CreateWindow(). Этот процесс одинаков во всех версиях Windows. В то время как класс окна определяет общие свойства всех окон данного семейства, параметры функции CreateWindowOзадают описание конкретного экземпляра окна. Если функция CreateWindow() выполняется успешно, она возвращает дескриптор созданного окна, в противном случае — null.
В файле SWP.Cфункция CreateWindow() вызывается следующим образом:
hWnd = CreateWindow(szProgNarae,
"SimpleWindowsProgram",
WSJDVERLAPPEDWINDOW, CW_USEDEFAOLT,
CW__USEDEFAULT, CW_DSEDEFAULT,
CWJJSEDEFAULT, (HWND)NULL, (HMENU)NULL,
(HANDLE) hlnst, (LPSTR)NDLL;
Первый параметр, szProgName, содержит название класса окна. За ним в кавычках указывается содержимое строки заголовка окна — "SimpleWindowProgram". Третий параметр задает стиль окна — ws_overlappedwindow. Это стандартный стиль Windows, определяющий обычное масштабируемое окно со строкой заголовка, системным меню, кнопками свертывания, развертывания и закрытия, а также с рамкой.
Следующие шесть параметров (cw_usedefault или null) определяют координаты начальной точки окна, его размеры, а также дескрипторы родительского окна и меню. Всем этим параметрам в нашем примере присвоены значения по умолчанию. Десятый параметр, hinst, содержит дескриптор экземпляра приложения. Окну не передаются никакие дополнительные значения, поэтому последний параметр равен NULL.
Отображение и обновление окна
Для отображения окна на экране предназначена функция ShowWindow(}:
ShowWindow(hWnd, nCmdShow);
Дескриптор окна hwnd былсоздан функцией CreateWindow (). Второй параметр, nCmdShow, устанавливает режим начального отображения окна. Так, задание константы sw_showminnoactive, определенной в файле WINUSER.H, приведет к отображению окна в виде значка на панели задач:
ShowWindow(hWnd, SW_SHOWMINNOACTIVE);
Константа sw_showmaximized отвечает за развертывание окна на весь экран. Режим sw_showminimized аналогичен режиму sw_showminnoactive за исключением того, что окно остается активным, т.е. имеет фокус.
Последний этап выведения окна на экран реализует функция UpdateWindow():
UpdateWindow(hWnd);
Вызов функции ShowWindow() с установленным параметром sw_shownormal приводит к заливке фона окна выбранной кистью. А функция UpdateWindow (генерирует сообщение wm_paint, указывающее на формирование содержимого рабочей области окна.
Цикл обработки сообщений
После отображения окна программа готова к выполнению своей непосредственной задачи — обработке сообщений. Вспомните, что в Windowsинформация от мыши или клавиатуры не передается напрямую приложению, а в виде сообщений помещается в очередь. Очередь может содержать сообщения как сгенерированные самой системой, так и поступившие от других приложений.
Как правило, цикл организуется с помощью такой конструкции:
while (GetMessage(&lpMsg, NULL, 0, 0) ) {
TranslateMessage(SlpMsg) ;
DispatchMessage(slpMsg);}
Функция GetMessage() читает очередное сообщение из очереди и записывает его в структуру msg, адресуемую указателем IpMsg.
Установка второго параметра равным NULLсвидетельствует о том, что читаться должны сообщения, адресованные любому окну приложения. Два других параметра формируют фильтр сообщений, который ограничивает получаемые сообщения определенным диапазоном (например, только сообщения мыши). Если эти параметры равны нулю, как в нашем случае, то принимаются любые сообщения, адресованные данному приложению.
После запуска цикла только одно сообщение может остановить его выполнение. Как только в цикл передается сообщение wm_quit, функция GetMessage() возвращает значение false, в результате чего цикл завершается, выполняются заключительные операции функции winMain() и работа приложения прекращается.
Функция TranslateMessage( )
Сообщения о нажатии виртуальных клавиш могут быть преобразованы в символьные сообщения с помощью функции TranslateMessage(), которая формирует сообщение wm_charиз пары wm_keydown/wm_keyup. Эта функция необходима только тем приложениям, которые обрабатывают ввод данных с клавиатуры. Важность функции заключается в том, что она позволяет пользователям выбирать команды меню путем нажатия клавиш, а не только щелчками тныши.
Функция DispatchMessage( )
Функция DispatchMessage() направляет полученное сообщение соответствующей оконной процедуре, автоматически определяя окно, которому адресовано сообщение.
Оконная процедура
Оконная процедура регистрируется в системе и вызывается всякий раз, когда Windowsвыполняет какую-либо операцию над окном приложения. В следующем фрагменте показан текст этой процедуры из файла SWP.C(за исключением нескольких специфичных для приложения строк):
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM IParam) {
HOC hdc;
PAINTSRUCT ps;
switch (messg) {
case WM_PAINT:
hdc = BeginPaint (hWnd, Sps);
ValidateRect(hWnd, NULL);
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return(DefWindowProc(hWnd, messg, wParam, IParam));
}
return(0);
}
Windowsпредполагает, что имя этой процедуры указанно в поле ipfnWndProcструктуры wcApp, описывающей класс окна. Все окна, созданные на базе данного класса, будут управляться процедурой WndProc().
В Windowsсуществует более двухсот различных сообщений, которые могут посылаться окнам. Все они имеют префикс wm_ (windowmessage— оконное сообщение), например: wm_create, wm_size, wm_paint.
Первым параметром процедуры WndProc() является дескриптор окна, которому посылается сообщение. Поскольку одна процедура может обрабатывать сообщения сразу нескольких окон, этот дескриптор позволяет определить, какому именно окну адресовано сообщение. .Второй параметр содержит идентификатор сообщения. Два оставшихся параметра, wParamи IParam, несут дополнительную информацию, зависящую от конкретного сообщения.
В нашей процедуре WndProc() определяются две локальные переменные: hdc, содержащая дескриптор контекста устройства (экрана), и ps, задающая структуру paintstruct, которая необходима для хранения информации, отображаемой в рабочей области окна.
Обработка самих сообщений осуществляется с помощью стандартной конструкции switch, которая может быть довольно большой (ее размер зависит от чиcла обрабатываемых сообщений).
Обработка сообщения WM_PAINT
Первое сообщение, которое в нашем примере обрабатывает процедура HndProc(), — это WM_PAINT. Поскольку Windows является многозадачной системой, то одно приложение может открыть свое окно поверх окна другого приложения. Это создает определенную проблему, так как при закрытии или перемещении верхнего окна нижнее не знает о происходящем, в результате чего Область, которую занимало верхнее окно, становится некорректной. Windows решает данную проблему путем направления сообщения wm_paintнижнему окну, информируя о том, что его содержимое требует обновления.
Помимо самого первого сообщения wm_paint, которое генерируется функцией UpdateWindow() внутри функции WinMain(), сообщение WM_PAINT также посылается в следующих ситуациях:
Любая часть окна, которая была закрыта другим объектом, например диалоговым окном, помечается как недействительная. Наличие недействительной области заставляет Windows послать приложению сообщение wm_paint, причем система отслеживает координаты такой области. Если окно оказалась закрыто несколькими объектами, Windowsвычислит координаты суммарной области. Другими словами, Windows не станет посылать несколько сообщений wm_paint для каждого отдельно взятого прямоугольного участка, а сгенерирует одно сообщение сразу для всей недействительной области сложной формы.
При обработке сообщения wm_paint вначале выполняется функция BeginPaint (), которая подготавливает указанное окно к операции рисования, заполняет структуру paintstruct данными об обновляемой области и возвращает дескриптор контекста устройства, связанного с этим окном.
Вызов функции invalidateRect() заставляет Windowsпометить указанную область окна как недействительную и сгенерировать для нее сообщение wm_paint. Приложение может получить координаты этой области, вызвав функцию GetUpdateRect (). Функция ValidateRect () удаляет указанный фрагмент из определения области, требующей перерисовки.
Функция EndPaint() вызывается всякий раз по завершении операций вывода данных в рабочую область окна. Функция ставит Windowsв известность, что приложение закончило обработку сообщений о перерисовке и имеющийся контекст устройства можно удалить.
Обработка сообщения WM_DESTROY
Когда пользователь выбирает в системном меню команду Close или нажимает кнодку закрытия окна, Windows посылает приложению сообщение WM_DESTROY. В ответ на это приложение завершает свою работу, вызывая функцию PostQuitMessage (), которая направляет в очередь сообщений приложения сообщение WM_QUIT, вследствие чего цикл обработки завершается.
Функция DefWindowProc( )
Функция DefWindowProcOобычно вызывается в блоке defaultоператора switch. Она возвращает все нераспознанные или необработанные сообщения обратно системе, гарантируя таким образом, что все посылаемые приложению сообщения будут корректно обработаны.
Создание проекта
Чтобы начать новый проект, выберите в окне компилятора в меню File команду New, а затем в открывшемся диалоговом окне New на вкладке Projects активизируйте элемент Win32 Application(рис. 17.1).
Рис. 17.1. Выбор типа проекта
Задайте для проекта имя SWP, щелкните на кнопке ОК, в появившемся окне мастера выберите опцию An empty project и вновь нажмите ОК, после чего будет создан пустой проект.
Чтобы добавить в проект программный файл, выберите в меню Project подменю Add To Project, а в нем — команду New(рис. 17.2).
Рис. 17.2. Для добавления в проект нового файла используется команда New
из подменю Add To Project
Воспользовавшись командой New, откройте диалоговое окно New(рис. 17.3), выделите
в нем элемент C++ SourceFile и введите имя файла (в нашем примере это
SWP.C).
Рис.17.3. Выбор типа добавляемого файла
Далее в окне редактора наберите текст программы (рис. 17.4).
Рис. 17.4. Исходный текст программы в окне редактора
Завершив ввод текста, сохраните файл, выбрав в меню File команду Save.
Следующий этап создания нового приложения состоит в построении исполняемого
файла. Но для начала важно правильно установить опции проекта. Выберите в
меню Project команду Settings.... Открывающееся при этом диалоговое
окно ProjectSettings содержит множество вкладок, на которых представлены
всевозможные опции сборки приложения. Убедитесь в том, что на вкладке General
в списке MicrosoftFoundationClasses выделена опция NotUsingMFC(рис.
17.5).
Рис. 17.5. Установки вкладки General
По умолчанию для проекта создаются две конфигурации — Debug(отладонная) и Release(финальная), из которых активной является первая, поэтому все файлы создаются и сохраняются в папке DEBUG. Когда вы будете уверены в корректности работы приложения и решите создать окончательную версию, выберите в меню Build команду Set Active Configuration иукажитеконфигурациюRelease.
На вкладке Link в поле Output file name указано, в какую папку будет помещен исполняемый файл и как он будет называться (рис. 17.6). Обратите внимание, что по умолчанию выходной папкой является DEBUG.
Рис. 17.6. Установки вкладки Link
Текст программы
Мы уже рассмотрели большую часть файла SWP.C. Ниже показан полный его текст.
/*
* swp.c
* Простейшее приложение Windows.
*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, DINT, WPARAM, LPARAM); char szProgName[] = "ProgName";
int WINAPI WinMain(HINSTANCE hlnst, HINSTANCE hPrelnst,
LPSTR IpszCmdLine, int nCmdShow) {
HWND hWnd;
MSG IpMsg;
WNDCLASS wcApp;
wcApp.lpszClassName = szProgName;
wcApp.hlnstance = hlnst;
wcApp.lpfnWndProc = WndProc;
wcApp.hCursor = LoadCursor(NULL, IDC_ARROW);
wcApp.hlcon = 0; wcApp.lpszMenuName = 0;
wcApp.hbrBackground = Getstockobject
(WHITE_BRUSH) ;
wcApp. style = CS_HREDRAW
| CS_VREDRAW;
wcApp.cbClsExtra = 0;
wcApp . obWndExtra = 0;
if ( IRegisterClass (swcApp) )
return 0;
{
hWnd = CreateWindow(szProgName, "Simple Windows Program", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAOLT,, CWJJSEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU)NULL, (HANDLE) hlnst, (LPSTR) NULL) ; ShowWindow(hWnd, nCmdShow) ; UpdateWindow(hWnd) ;
while (GetMessage (SlpMsg, 0, 0,
0)){ TranslateMessage(SlpMsg) ;
DispatchMessage (SlpMsg) ; }
return (IpMsg.wParam) ;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM IParam) {
HOC hdc;
PAINTSTRUCT ps;
switch (messg)
{case WM_PAINT:
hdc = BeginPaint(hWnd, Sps) ;
MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, 639,429); MoveToEx(hdc, 300, 0, NULL); LineTo(hdc, 50, 300);
TextOut(hdc, 120,-30,"<-a few lines ->",17);.
ValidateRect(hWnd, NULL);
EndPaint(hWnd, sps);
break; case WM_DESTROY:
PostQuitMessage(0);
break; default:
return(DefWindowProc(hWnd, messg, wParam, IParam));
break; }
Вспомните, что основная часть приведенного кода необходима для регистрации класса окна и создания самого окна. Блок программы, отвечающий за вывод содержимого окна, достаточно прост:
MoveToEx(hdc. О, О, NULL);
LineTo{hdc, 639,429);
MoveToEx(hdc, 300, О, NULL);
LineTofhdc, 50, 300);
TextOut(hdc, 120, 30, "<- a few lines ->",17);
В нескольких следующих параграфах мы будем модифицировать текст этого блока, чтобы поэкспериментировать с различными встроенными функциями Windows, предназначенными для рисования графических объектов GDI, которые называются графическими примитивами.
А пока что скомпилируйте и скомпонуйте приложение с помощью команды Build
или RebuildAll меню Build, после чего посредством команды Execute
запустите программу на выполнение. Окно приложения показано на рис. 17.7.
Программа осуществляет вывод в окно двух линий и небольшого сообщения.
Рис. 17.7. Окно программы
Рисование эллипса
Функция Ellipse() используется для построения эллипса или окружности. Центр фигуры будет одновременно и центром воображаемого прямоугольника, в который вписывается эллипс. Координаты этого прямоугольника: xl,ylи х2,у2 .
Эллипс представляет собой замкнутую фигуру. Внутренняя область эллипса закрашивается цветом текущей кисти. Дескриптор контекста устройства задается параметром hdc. Все остальные параметры имеют тип int. Функция в случае успешного завершения возвращает значение true.
Синтаксис функции Ellipse() таков:
Ellipse (hdc, xl, yl, x2, y2}
В приведенном ниже фрагменте программы создаются небольшой эллипс и сопровождающая его надпись (рис. 17.9):
Ellipse(hdc, 200, 200, 275, 250);
TextOut(hdc, 210, 215, "<-an ellipse", 13);
Рис. 17.9. Эллипс, нарисованный в окне приложения
Рисование сегмента
Функция chord() рисует замкнутую дугу, конечные точки которой соединены линией с координатами хЗ,уЗ и х4,у4. Построение фигуры осуществляется против часовой стрелки (от точки хЗ,уЗ до точки х4,у4). Внутренняя область фигуры закрашивается цветом активной кисти. Дескриптор контекста устройства задается параметром hdc. Все остальные параметры имеют тип int. В случае успешного завершения функция возвращает значение
TRUE
Синтаксис функции Chord():
Chord (hdc, xl,
yl, x2, y2, хЗ, уЗ,
х4, y4)
В приведенном ниже фрагменте программы создается сегмент и сопровождающая его надпись (рис. 17.11):
Chordfhdc, 550, 20, 630,80, 555, 25,625,70);
TextOutfhdc, 470, 30, " A Chord ->",11);
Рис. 17.11. Сегмент, нарисованный в окне приложения
Рисование сектора
Для рисования сектора предназначена функция Pie(). Центр эллипса, из которого вырезается сектор, будет одновременно и центром воображаемого прямоугольника, в который вписывается эллипс. Координаты этого прямоугольника — x1,y1 и х2,у2. Концы сектора обозначаются точками с координатами хЗ,уЗ и х4,у4. Построение фигуры осуществляется против часовой стрелки (от точки хЗ,уЗ до точки х4,у4). Внутренняя область сектора закрашивается цветом активной кисти. Дескриптор контекста устройства задается параметром hdc. Все остальные параметры имеют тип int. В случае успешного завершения функция возвращает значение true.
Синтаксис функции Pie():
Pie (hdc, xl, yl, x2, y2, хЗ, уЗ, х4, y4)
В приведенном ниже фрагменте программы создается сектор и сопровождающая его надпись (рис. 17.13):
Pie(hdc, 300, 50,-400, 150, 300,
50,300, 100);
TextOut(hdc, 350, 80, "<-A Pie Wedge", 14);
Рис. 17.13. Сектор, нарисованный в окне приложения
Рисование прямоугольника
Функция Rectangle() предназначена для построения прямоугольника или квадрата с использованием двух точек с. координатами xl,ylи х2,у2. Получаемая замкнутая фигура закрашивается цветом текущей кисти. Дескриптор контекста устройства задается параметром hdc. Все остальные параметры имеют тип int. В случае успешного завершения функция возвращает значение true.
Синтаксис функции Rectangle ():
Rectangle (hdc, xl, yl, x2, y2)
В приведенном ниже фрагменте программы создается прямоугольник и сопровождающая его надпись (рис. 17.14):
Rectangle(hdc, 50, 300, 150, 400);
TextOut(hdc, 160,350, "<- A Rectangle", 14);
Рис. 17.14. Прямоугольник, нарисованный в окне приложения
Использование файла SWP.C в качестве шаблона
В предыдущих параграфах описаны принципы разработки простейшего Windows-приложения, которое теперь может послужить шаблоном для создания других приложений. Ниже показан текст приложения, строящего синусоиду.
/*
* Sine.с
* Программа, которая рисует синусоиду.
* Построена на базе файла swp.c.
*/
#include <windows.h>
#include <math.h>
#define pi 3.14159265359
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); : char szProgName[]="ProgName";
int WINAPI WinMain (HINSTANCE hlnst, HINSTANCE hPrelnst, , ,., LPSTR IpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG IpMsg;
WNDCLASS wcApp;
wcApp.lpszClassName = szProgName; wcApp.hlnstance = hlnst; wcApp.lpfnWndProc = WndProc;
wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.hlcon = NULL; wcApp.lpszMenuName = NULL;
wcApp.hbrBackground = GetStockObject(WHITE_BRUSH); wcApp. style = CS_HREDRAW | CS_VREDRAW; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; if (IRegisterClass(SwcApp)) return 0;
hWnd = CreateWindow(szProgName, "A Sine Wave",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)NULL,.(HMENU)NULL, (HANDLE)hlnst, (LPSTR)NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
while (GetMessage(&lpMsg, 0, 0 , 0) ) { TranslateMessage(SlpMsg); DispatchMessage(SlpMsg);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, DINT messg,
WPARAM wParam, LPARAM IParam) {
HOC hdc;
PAINTSTRUCT ps;
double y; int i;
{
switch (messg) {
case WM_PAINT:
hdc=BeginPaint(hWnd, Sps) ;
/* построение осей координат */
MoveToEx(hdc, 100, 50, NULL);
LineTo(hdc, 100, 350);
MoveToEx(hdc, 100, 200, NULL);
LineTo(hdc, 500, 200);
MoveToEx(hdc, 100, 200, NULL);
/* построение синусоиды */
for (i=0; i < 400; i++) {
у = 120.0*sin(pi*i*(360.0/400.0)/180.0) LineTo(hdc, i+100, (int)(200.0-y));
}
ValidateRect (hWnd, NULL);
EndPaint (hWnd, Sps);
break; case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return (DefWindowProc (hWnd, messg, wParam, IParam) ) ;
break; } return (0);
}
Если проанализировать текст файла SINE. С и сравнить его с текстом рассмотренного ранее файла SWP.C, то можно увидеть, что внесенные изменения действительно незначительны.
Обратите внимание, что в процедуре WndProc( ) определяются две новые переменные:
double у; int i;
Оси координат строятся с помощью функций MoveToEx( ) и LineTo( ) :
/* построение осей координат */
MoveToEx(hdc, 100, 50, NULL);
LineTo (hdc, 100, 350);
MoveToEx (hdc, 100, 200, NULL) ;
LineTo (hdc, 500, 200);
MoveToEx (hdc, 100, 200, NULL) ;
Синусоида рисуется в цикле for. Ее амплитуда составляет 120 пикселей относительно горизонтальной оси. Для построения синусоиды используется функция sin( ) , объявленная в файле МАТН.Н. Константа piнеобходима для преобразования значений углов из градусов в радианы.
/* построение синусоиды */
for(i=0;i < 400; i++)
{
у = 120. 0*sin(pi*i* (360.0/400.0)/180.0);
LineTo(hdc, i+100, (int)(200.0-y));
Окно программы показано на рис. 17.15. Поскольку в приложении
не делалось никаких предположений о возможных размерах экрана, на мониторах
с разной разрешающей способностью физические размеры изображения могут оказаться
разными, что, как правило, нежелательно. Ниже, на примере приложения PIE.C,
вы увидите, как можно избежать этого недостатка.
Рис. 17.15. Синусоида, выведенная на экран программой SINE.C
Создание диаграмм
Круговые диаграммы широко применяются в коммерческих приложениях при создании различного рода презентаций. В представленной ниже программе будут объединены многие приемы, рассмотренные в этой и предыдущей главах. В частности, в программе задействованы меню и разработанные нами диалоговые окна Aboutи PieChartData. В окне ввода данных пользователю будет предложено задать до десяти значений, определяющих размеры секторов. На основании введенных целочисленных значений будут вычислены угловые размеры секторов в пропорции к общему размеру круга — 360 градусов. Минимальный размер сектора — 1 градус. Для закрашивания секторов определен массив IColor[] с набором цветов. Пользователь может также ввести подпись к диаграмме.
Для работы над данным приложением необходимо иметь четыре файла: PIE.H, PIE.RC, PIE.Cи PIE.CUR. Последний, содержащий изображение указателя мыши, вы можете разработать самостоятельно с помощью редактора ресурсов.
Ниже показан текст файла заголовков PIE.H, в котором содержатся идентификаторы команд меню и элементов управления диалоговых окон:
#define IDM_ABOUT 10
#define IDM_INPUT 20
#define DM_TITLE 160
#define DM_P1161
#define DM_P2162
#define DM_P3163
#define DM_P4164
#define DM_P5165
#define DM_P6166
#define DM_P7167
#define DM_P8168
#define DM_P9169
#define DM_P10170
В файле ресурсов PIE.RC описываются меню и два диалоговых окна (более подробно об этих ресурсах говорилось в предыдущей главе), а также ряд вспомогательных ресурсов.
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////
#include "pie.h"
#define APST0DIO_HIDDEN_SYMBOLS
#include "windows. h"
tundef APSTUDIO_HIDDEN_SYMBOLS
//////////////////////////////////////
tundef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////
// Ресурсы для английского (США) языка
#if !defined(AFX_RESOURCE_DLL) || defined (AFX_TARG_END) -
tifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
tpragma code_page (1252)
#endif //_WIN32
////////////////////////////////////////
//
// Указатель мыши
//
PIECURSOR CURSOR DISCARDABLE "pie.cur"
////////////////////////////////////////
//
// Меню
//
PIEMENU MENU DISCARDABLE BEGIN
POPUP "Pie_Chart_Data"
BEGIN
MENUITEM "About...", IDM_ABOUT
MENUITEM "Input...", IDM_INPUT
MENUITEM "Exit", IDM EXIT
END
/////////////////////////////////////////
// Диалоговые окна
//
ABOUTDLGBOX DIALOG DISCARDABLE 50,300, 180, 84
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE I WS_CAPTION I WS_SYSMENU
CAPTION "About"
FONT 8, "MS Sans Serif"
BEGIN
CTEXT "Microsoft СPie Chart Program", -1,3, 29,176,10
CTEXT "by William H. Murray and Chris H. Pappas", -1, 3, 16,176,10
PUSHBUTTON "ОК", IDOK, 74,51,32,14
END
PIEDLGBOX DIALOG DISCARDABLE 93,37,195,159
STYLE DS_MODALFRAME | WS_POPUP I WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Pie Chart Data"
FONT 8, "MS Sans Serif"
BEGIN
GROUPBOX "Chart Title:",100,
5, 3, 182, 30, WSJTABSTOP
GROUPBOX "Pie Wedge Sizes:",101, 3, 34,187, 95,WS_TABSTOP
LTEXT "Title: ", -1,10,21,30,8
EDITTEXT DMJTITLE, 40,18,140, 12
LTEXT "Wedge #1:", -1,
10, 50, 40,8, NOT WS_GROUP
LTEXT "Wedge #2:", -1, 10, 65, 40,8. NOT WS_GROUP
LTEXT "Wedge #3:", -1, 10, 80, 40, 8, NOT WS_GROUP
LTEXT "Wedge t4: ", -1, 10, 95, 40, 8, NOT WS_GROUP
LTEXT "Wedge #5:", -1, 10, 110, 40,8, NOT WS_GROUP
LTEXT "Wedge #6:", -1, 106,50, 40,8, NOT WS_GROUP
LTEXT "Wedge #7:", -1, 106,65, 40,8. NOT WS_GROUP
LTEXT "Wedge #8:", -1, 106,80, 40,8, NOT WS_GROUP
LTEXT "Wedge #9:", -1, 106,95, 40, 8, NOT WS_GROUP
LTEXT "Wedge #10:", -1,102, 110, 45,8, NOT WS_GROUP
EDITTEXT DM_P1, 55, 45, 30,12
EDITTEXT DM_P2, 55, 60, 30, 12
EDITTEXT DM_P3, 55, 75, 30, 12
EDITTEXT DM_P4, 55, 90, 30, 12
EDITTEXT DM_P5, 55, 105, 30, 12
EDITTEXT DM_P6, 150, 44, 30,12
EDITTEXT DM_P7, 150, 61, 30,12
EDITTEXT DM_P8, 150, 76, 30,12
EDITTEXT DM_P9, 149,91, 30,12
EDITTEXT DM_P10, 149,106,30, 12
PUSHBUTTON "ОК", IDOK, 39,135, 24,14
PUSHBUTTON "Cancel", IDCANCEL,
122, 136,34,14
END
tifdef APSTUDIO_INVOKED
///////////////////////////////////////////
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0" END
2 TEXTINCLUDE DISCARDABLE BEGIN
"#include ""pie.h""\r\n"
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n".
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0" END
#endif // APSTUDIO_INVOKED
#endif // Ресурсы для английского (США) языка
Следует помнить, что файл PIE.RC— это просто текстовый эквивалент описаний меню и диалоговых окон, созданных с помощью редактора ресурсов (см. предыдущую главу).
Файл PIE.С содержит основной текст программы. Несмотря на достаточно большой его размер, вы легко можете найти здесь знакомые блоки из файла SWP.C.
/*
* PIE.С
* Создание круговых диаграмм.
*/
#include <windows.h>
#include <string.h>
#include <math.h>
#include "pie.h"
#define radius 180
#define maxnurawedge 10
#define pi 3.14159265359
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK PieDlgProc(HWND, UINT, WPARAM, LPARAM);
char szProgName[] = "ProgName"; char szApplName[] = "PieMenu"; char szCursorName[] = "PieCursor";
char szTString[80] = "(piechart title area)";
unsigned int iwedgesize[maxnumwedge] = {5,20,10,15};
long IColor[maxnumwedge] = {OxOL,OxFFL, 'OxFFOOL, OxFFFFL,
OxFFOOOOL, OxFFOOFFL, OxFFFFOOL, OxFFFFFFL, OxSOSOL, Ox808080L);
int WINAPI WinMain(HINSTANCE hlnst, HINSTANCE hPrelnst,
LPSTR IpszCmdLine, int nCmdShow) {
HWND hWnd;
MSG IpMsg;
WNDCLASS wcApp;
wcApp.lpszClassName = szProgName; wcApp.hlnstance = hlnst; wcApp.lpfnWndProc = WndProc;
wcApp.hCursor = LoadCursor (hlnst, szCursorName); wcApp.hlcon = Ldadlcon(hlnst,szProgName); wcApp.lpszMenuName = szApplName;
wcApp.hbrBackground = GetStockObject(WHITE_BRUSH); wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; if (IRegisterClass(SwcApp)) return 0;
hWnd = CreateWindow(szProgName, "PieChart Application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)NULL, (HMENU)NULL, (HANDLE)hlnst, (LPSTR)NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(SlpMsg, 0, 0, 0)) {
TranslateMessage(SlpMsg); DispatchMessage(SlpMsg); )
return(IpMsg.wParam); }
BOOL CALLBACK AboutDlgProc(HWND hdlg, UINT messg,
WPARAM wParam, LPARAM IParam) I
switch (messg) {
case WM_INITDIALOG:
break;
case WM_COMMAND: switch (wParam)
{
case IDOK:
EndDialog(hdlg, TRUE),
break; default:
return FALSE; }
break; default:
return FALSE; } return TRUE;
}
BOOL CALLBACK PieDlgProc(HWND hdlg, UINT messg,
WPARAM wParam, LPARAM IParam) {
switch (messg) {
case WM_INITDIALOG:
return FALSE; case WM_COMMAND: switch (wParam) {
case IDOK:
GetDlgltemText(hdlg, DMJTITLE, szTString, 80); iWedgesize[0] = GetDlgltemlnt(hdlg, DM_P1, NULL, 0) iWedgesize[l] = GetDlgltemlnt(hdlg, DM_P2, NULL, 0) iWedgesize[2] = GetDlgltemlnt(hdlg, DM_P3, NULL, 0) iWedgesize[3] = GetDlgltemlnt(hdlg, DM_P4, NULL, 0) iWedgesize[4] = GetDlgltemlnt(hdlg, DM_P5, NULL, 0) iWedgesize[5] = GetDlgltemlnt(hdlg, DM_P6, NULL, 0) iWedgesize[6] = GetDlgltemlnt(hdlg, DM_P7, NULL, 0) iWedgesize[7] = GetDlgltemlnt(hdlg, DM_P8, NULL, 0) iWedgesize[8] - GetDlgltemlnt(hdlg, DM_P9, NULL, 0) iWedgesize[9] = GetDlgltemlnt(hdlg, DM_P10, NULL, 0); EndDialog(hdlg, TRUE); break;
case IDCANCEL:
EndDialog(hdlg, FALSE); break;
default:
return FALSE; }
break; default:
return FALSE; } return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM IParam) {
HDC hdc;
PAINTSTRUCT ps;
HBRUSH hBrush;
static HINSTANCE hlnstl, hlnst2;
static int xClientView, yClientView;
unsigned int iTotalwedge[maxnumwedge+1]; int i, iNWedges;
iNWedges = 0;
ford = 0; i. < maxnurawedge; i++) {
if<iWedgesize[i] != 0) iNWedges++; } iTotalwedge[0] = 0;
for(i = 0; i < iNWedges; i++)
iTotalwedge[i+1] = iTotalwedge[i] + iWedgesize[i];
switch (messg) (
case WM_SIZE:
xClientView - LOWORD(IParam); yClientView = HIWORD(IParam); break; case WM_CREATE:
hlnstl = ((LPCREATESTRUCT) IParam)->hlnstance; hlnst2 = ((LPCREATESTRUCT) IParam)->hlnstance; break; case WM_COMMAND:
switch (wParam) {
case IDM_ABOUT:
DialogBox(hlnstl, "AboutDlgBox", hWnd, (DLGPROC) AboutDlgProc)i break/case IDM_INPUT:
DialogBox(hlnst2, "PieDlgBox", hWnd, (DLGPROC) PieDlgProc); InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); break; case IDM_EXIT:
SendMessage(hWnd, WM_CLOSE, 0, OL);
break; default:
break; }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, Sps);
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowExtExfhdc, 500, 500, NULL);
SetViewportExtEx(hdc, xClientView, -yClientView, NULL);
SetViewportOrgEx(hdc, xClientView/2, yClientView/2, NULL);
if(xClientView > 200)
TextOut(hdc, strlen (szTString) * (-8/2),
240, szTString, strlen (szTString));
for(i= 0; i < iNWedges; i++){ ' hBrush = CreateSolidBrush.(lCo;l.or [i]) ; SelectObject(hdc, hBrush); Pie(hdc, -200, 200, 200, -20:0,,.. :
(int)(radius*cos(2*pi*iTotalWedge[i]/
iTotalWedgeJiNWedges])),
(int)(radius*sin(2*pi*iToialWedge[iJ/
ITotalWedge[iNWedges])),
(int)(radius*cos(2*pi*iTotalWedge[i+1]/
iTotalWedge tiNWedges])),
(int)(radius*sin(2*pi*iTotalWedge[i+l]/
iTotalWedge[iNWedges]))); }
ValidateRect (hWnd, NULL);
EndPaint (hWnd, Sps); 1 break; case WM_DESTROY:
PostQuitMessage (0);
break; default:
return (DefWindowProc (hWnd, messg, wParam, IParam) ) ;
)
return(0);
Ниже мы несколько подробнее проанализируем содержимое приведенных файлов.
Файл PIЕ.Н
Файл заголовков приложения содержит уникальные идентификаторы команд меню. Обратите внимание на идентификаторы с префиксом ом_, представляющие поля диалогового окна, в которые пользователь будет вводить значения.
Файл РIЕ.RС
Файл ресурсов приложения содержит команду подключения указателя мыши (piecursor),
а также описания меню (piemenu) и двух диалоговых окон (aboutdlgbox и piedlgbox).
На рис. 17.16 показано диалоговое окно About.
Рис. 17.16. Окно About
Процесс создания этих окон был рассмотрен в предыдущей главе. В описании диалогового окна для каждого элемента управления задаются его размеры и координаты на экране. Редактор ресурсов вычисляет эти значения автоматически. Впрочем, вы можете просто ввести данный файл в текстовом виде, а редактор скомпилирует его и воссоздаст описанные в нем ресурсы.
Программа, записанная в файле PIE.C, создает круговую диаграмму, содержащую до десяти секторов. Если в меню выбрать команду Input, откроется диалоговое окно PieChartData, в котором пользователь может установить число секторов и задать их относительную ширину, а также ввести заголовок диаграммы. Эти данные передаются в программу после щелчка на кнопке ОК. Информация, поступающая от диалоговых окон, обрабатывается в ветви caseIDOKпосредством процедуры PieDlgProcO. Текст заголовка возвращается функцией GetDlgltemText(), а числовые значения — функцией GetDlgltemlnt(). Последняя принимает четыре аргумента. Первые два — это дескриптор окна и идентификатор элемента управления. Третий аргумент является указателем на булеву переменную, устанавливаемую в TRUEпри успешном завершении функции. В нашем случае для простоты его значение задано равным null. Четвертый аргумент определяет, следует ли вводимое значение интерпретировать как знаковое (аргумент не равен нулю) или беззнаковое (аргумент равен нулю). Введенные значения сохраняются в глобальном массиве iWedgesize[ ].
Основная работа выполняется в процедуре WndProc(), где обрабатываются пять сообщений: wm_size, wm_create, wm_command, wm_paintи wmjdestroy. Сообщение wm_sizeпосылается всякий раз, когда изменяются размеры окна приложения. Информация о размерах сохраняется в переменных xciientviewи yClientViewи впоследствии, при обработке сообщения wm_paint, используется для настройки масштаба диаграммы.
В процессе обработки сообщения wm_createсоздаются два дескриптора приложения, hlnstlи hlnst2, которые затем передаются функции DialogBox(), генерирующей экземпляры двух диалоговых окон.
При выборе какой-либо команды меню генерируется сообщение wm_command. Если это сообщение имеет подтип IDM_ABOUT, открывается окно About, а если подтип idm_input— окно ввода данных. При получении сообщения idm_exitприложение закрывает свое главное окно и завершает работу.
Код рисования непосредственно диаграммы содержится в блоке обработки сообщения wm_paint. По умолчанию в системе установлен режим отображения мм_техт. В этом режиме координаты объектов отсчитываются (в пикселях) начиная с верхнего левою угла окна, имеющего координаты 0,0. Вот почему в программе SINE.Cвид изображения будет меняться всякий раз при изменении разрешения экрана. В программе PINE.Cустанавливается режим отображения mm_isotropic.
SetMapMode(hdc, MM_ISOTROPIC) ; SetWindowExtEx(hdc, 500, 500, NULL);
SetViewportExtEx(hdc, xClientView, -yClientView, NULL); SetViewportOrgEx(hdc, xClientView/2, yClientView/2, NULL);
Режимы отображения, существующие в Windows, перечислены в табл. 17.4.
Таблица 17.4. Режимы отображения | |
Режим | Описание |
mm_anisotropic | Одной логической единице соответствует произвольная физическая единица; масштабирование по осям х и у независимое |
mm_hienglish | Одной логической единице соответствует 0,001 дюйма; ось у направлена снизу вверх |
mm_himetric | Одной логической единице соответствует 0,01 мм; ось у направлена снизу вверх |
mm_isotropic | Одной логической единице соответствует произвольная физическая единица; масштаб по осям х и у одинаков |
mm_loenglish | Одной логической единице соответствует 0,01 дюйма; ось у направлена снизу вверх |
mm_lometric | Одной логической единице соответствует 0,1 мм; ось у направлена снизу вверх |
MM_TEXT | Одной логической единице соответствует один пиксель; ось у направлена сверху вниз (это режим по умолчанию) |
MM_TWIPS |
Одной логической единице соответствует 1/20 точки принтера (1/1440 дюйма); ось у направлена снизу вверх |
Режим mm_isotropic позволяет программисту самостоятельно выбирать масштаб изображения по осям х и у. Функция SetWindowExtEx( ) задает размеры окна по горизонтали и вертикали (в нашем примере они равны 500). Эти логические размеры автоматически преобразуются системой в соответствии с физическими размерами экрана. В функции SetviewportExtEx( ) устанавливается размер области просмотра (в пикселях). Отрицательное значение координаты у означает, что ось у направлена снизу вверх. Система сравнивает размеры окна и области просмотра и вычисляет соотношение между логическими единицами и пикселями. Функция SetViewportOrgEx( ) устанавливает координаты точки начала области просмотра (в пикселях). В нашем примере это будет центр окна. После вызова этих функций все последующие координаты можно указывать в логических единицах.
Заголовок диаграммы выводится уже с учетом текущего режима отображения. Если размеры окна слишком малы, заголовок не показывается вообще.
if (xClientView > 200)
TextOutfhdc, strlen(szTString)*(-8/2) , 240, szTString,. ,,1" strlen (szTString) );
Прежде чем приступать к построению секторов диаграммы, давайте вернемся к началу процедуры WndProc и выясним, каким образом вычисляются относительные размеры секторов.
В следующем фрагменте определяется, из скольких секторов будет состоять круг:
iNWedges=0;
for(i=0;i < maxnumwedge; i++)
{ if (iWedgesizeli] != 0) iNWedges++;}
Приращение переменой iNWedgesвыполняется каждый раз, когда в массиве iWedgesize [i] обнаруживается ненулевое значение. Таким образом, по окончании цикла будет известно число секторов диаграммы.
Накопительные размеры секторов хранятся в массиве iTotalWedge[] . Эти значения необходимы для вычисления границ между секторами. Например, если пользователь ввел в качестве размеров секторов значения 5, 10, 7 и 20, то в массиве iTotalWedge[] .. будут представлены значения 0, 5, 15, 22 и 42. Вот как они получаются:
iTotalWedge[0] = 0;
for(i= 0; i < iNWedges; i++)
iTotalWedge[i+l] = iTotalWedge [i]+ iWedgesize [i];
Элементы массива iTotalWedge[ ] используются для вычисления начального и конечного углов каждого сектора. Вспомните, что функция Pie( ) принимает девять параметров. Первый является дескриптором контекста устройства. Следующие четыре определяют координаты прямоугольника, в который вписывается эллипс. В нашем примере выбраны координаты -200,200 и 200,-200. Оставшиеся четыре параметра определяют координаты начальной и конечной точек сектора. Координата х вычисляется с помощью функции cos( ) , а координата у — с помощью функции sin( ) . Так, начальную координату х первого сектора можно определить как произведение радиуса окружности на косинус значения 2*pi*iTotalWedge[0] . Коэффициент 2*piздесь необходим для преобразования градусов в радианы. Координаты конечной точки сектора определяются по тому же алгоритму, но для их вычисления используется следующий элемент массива iTotalWedge[] — iTotalWedge[1 ]. Чтобы все секторы вписались в круг, значение координаты делится на общий накопительный размер всех секторов — iTotalWedge [iNWedges].
for(i = 0; i < iNWedges; i++)
{
hBrush = CreateSolidBrush(lColor [i]) ;
SelectObject (hdc, hBrush);
Pie(hdc, -200, 200, 200, -200,
(int)(radius*cos (2*pi*iTotalWedge [i]/
iTotalWedge [iNWedges] )), (int)(radius*sin (2*pi*iTotalWedge [i]/
iTotalWedge [ iNWedges] )), (int)(radius*cos(2*pi*iTotalWedge[i+l] / iTotalWedge [iNWedges] ),
(int)(radius*sin(2*pi*iTotalWedge[i+l]
/ ,
iTotalWedge [ iNWedges ]);
}
Весь цикл повторяется столько раз, сколько указано в переменной iNWedges.
Диаграмма, представленная на рис. 17.18, строится по умолчанию, а показанная
на рис. 17.19 — на основании значений, введенных пользователем.
Рис. 17.18. Диаграмма, построенная по умолчанию
Рис. 17.19. Диаграмма, построенная с учетом введеных пользователем данных