Win32 api программирование
Скачать 0.78 Mb.
|
1.7.2. Вывод текстовых строк В рассматриваемом примере (см. п. 1.7.4) рисование на экране сво- дится к выводу текста текущая дата и время с помощью функции Draw- Text(), которая имеет следующий прототип: int DrawText( HDC hDc, //дескриптор контекста устройства LPCTSTR lpString, //указатель на символьную строку int nLenght, //длина текста LPRECT lpRect, //указатель на ограничивающий прямоугольник UINT uFormat //флаги форматирования текста ); Функция выводит текст из строки lpString в прямоугольную об- ласть, заданную структурой типа RECT. Если значение параметра nLenght установить в −1, то функция сама определит длину строки по завершающему нулевому символу. Для получения размеров клиентской области окна (ограничиваю- щий прямоугольник) следует вызвать функцию GetClientRect(): BOOL GetClientRect( HWND hWnd, //дескриптор окна LPRECT lpRect //адрес структурной переменной //типа RECT ); Функция GetClientRect() копирует параметры прямоугольника, ограничивающего клиентскую часть окна, в структурную переменную типа RECT. Параметры даются относительно левого верхнего угла кли- ентской области окна, поэтому поля left и top всегда равны ну- лю, а поля right и bottom содержат ширину и высоту клиентской об- ласти в пикселях. Метод форматирования задается параметром uFormat, который может быть задан, например, комбинацией следующих флагов: DT_SINGLELINE | DT_CENTR | DT_VCENTR где DT_SINGLELINE показывает, что текст будет выводиться в одну строку; DT_CENTR – текст будет выводиться по центру относительно го- ризонтали прямоугольной области; DT_VCENTR – относительно вертика- ли 32 1.7.3. Атрибуты цвета и фона выводимого текста Цвет объектов задается 32-битным числом (тип COLORREF), на- пример: 0x00bbggrr, где b – синяя, g – зеленая, r – красная состав- ляющая цвета. Для создания цвета можно использовать макрос RGB(R,G,B) Для установки цвета текста (text color) применяется функция SetTextColor(), прототип которой имеет вид COLORREF SetTextColor(HDC hDc, COLORREF crColor); Для установки цвета фона графического элемента (background color), т.е. цвета, который отображается под каждым символом, исполь- зуется функция SetBkColor(), имеющая следующий прототип: COLORREF SetBkColor(HDC hDc, COLORREF crColor); Для обеих функций первый параметр hDc – контекст устройства, второй crColor – цвет текста или фона соответственно. Обе функции возвращают ссылку на предыдущий цвет. Для установки режима смешивания фона (background mix mode) используется функция SetBkMode, прототип которой int SetBkMode(HDC hDc, int uMode); Второй параметр uMode функции может принимать следующие значения: OPAQUE – непрозрачный режим, т.е. цвет фона графического элемента выводится поверх существующего фона окна; TRANSPARENT – прозрачный, т.е. цвет фона графического элемента игнорируется, а сим- вол выводится на существующем фоне окна. Если перечисленные выше функции не вызывались, то в контексте устройства будут использоваться по умолчанию следующие значения: цвет текста – черный, цвет фона графического элемента – белый, режим смешивания фона – OPAQUE. 1.7.4.Таймеры Windows Операционная система Windows предоставляет программисту функции, позволяющие установить в приложении требуемое количест- во программных таймеров. С помощью таймеров приложение, напри- мер, может обеспечить временную синхронизацию и задание времен- ных интервалов. Функция SetTimer() создает или видоизменяет системный таймер, который формирует сообщение WM_TIMER. UINT SetTimer( HWND hWnd, //Дескриптор окна для которого //создается таймер UINT uTimerID, //Идентификатор таймера UINT uInterval,//Значение временного интервала //в милисекундах TIMERPROC fnTimerProc //Указатель на функцию //обратного вызова ); 33 Таймер после своей установки начинает периодически с интерва- лом uInterval генерировать сообщение WM_TIMER, которое посту- пает в окно(hWnd). Параметр fnTimerProc представляет собой CALLBACK– функцию, которая должна быть определена в программе и содержать процедуру прикладной обработки прерывания от таймера. Если этот параметр не NULL , то при каждом срабатывании таймера операцион- ная система будет непосредственно, в обход оконной функции , вызывать функцию fnTimerProc. Если учесть, что сооб- щение WM_TIMER посылается функцией DispatchMessage() в оконную функцию в последнюю очередь, т.е. тогда когда в очереди со- общений приложения нет других сообщений, а CALLBACK – функция вызывается непосредственно. Следовательно, CALLBACK– функция обеспечивает более оперативную обработку сигналов от таймера. Ее прототип: VOID CALLBACK TimerProc( HWND hWnd, //Дескриптор окна UINT uMsg, //WM_TIMER UINT_PTR idEvent, //Идентификатор таймера DWORD dwTime //Количество миллисекунд, прошедее //с момента запуска Windows ); Если сообщение от таймера предпологается обрабатывать по- средством оконной функции, то на месте последнего параметра функ- ции SetTimer() указывается NULL. Функция KillTimer()удаляет таймер. BOOL KillTimer( HWND hWnd, //Дескриптор окна UINT_PTR idEvent //Идентификатор таймера ); 1.7.5. Приложение с главным окном Эту программу можно использовать в качестве каркаса Windows- приложения с главным окном. /*Операторы препроцессора*/ /* #define UNICODE #ifdef UNICODE #define _UNICODE #endif */ #define STRICT #include #include /* Файл tchar.h состоит из макросов, которые ссылаются на UNICODE данные и функции, если определен макрос UNICODE, или на ANSI 34 данные и функции, если этот макрос не определен, кроме того он полностью заменяет файл string.h */ #include /*Прототип оконной функции*/ LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); /* Прототип функции получения текущего времени и преобразование его в символы */ void OutTimeDate(HWND); /* Массив для формирования строки - текущие дата и время */ TCHAR szCurrentTime[40]; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { /*Произвольный заголовок окна*/ TCHAR szTitle[]=_TEXT("ИТМО"); /*Произвольное имя класса*/ TCHAR szWindowClass[]=_TEXT("QWERTY"); /* Структурная переменная msg типа MSG для получения сообщений */ MSG msg; /* Структурная переменная wcex типа WNDCLASSEX для задания характеристик окна */ WNDCLASSEX wcex; HWND hWnd; //Дескриптор главного окна /*Проверяем, было ли это проложение запущено ранее*/ if(hWnd=FindWindow(szWindowClass,NULL)) { /*Проверяем, было ли это окно свернуто в пиктограмму*/ if(IsIconic(hWnd)) ShowWindow(hWnd,SW_RESTORE); /*Выдвигаем окно приложения на передний план*/ SetForegroundWindow(hWnd); return FALSE; } /*Обнуление всех членов структуры wcex*/ memset(&wcex,0,sizeof(WNDCLASSEX)); /*Регистрируем класс главного окна*/ wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; /*Определяем оконную процедуру для главного окна*/ wcex.lpfnWndProc = (WNDPROC)WndProc; //wcex.cbClsExtra = 0; //wcex.cbWndExtra = 0; wcex.hInstance = hInstance;//Дескриптор приложения /* Стандартная пиктограмма, которую можно загрузить функцией LoadIcon(hInstance, MAKEINTRESOURCE(IDI_?_?)) 35 */ wcex.hIcon = (HICON)LoadImage(hInstance, IDI_APPLICATION, IMAGE_ICON,32,32,0); /*Стандартный курсор мыши*/ wcex.hCursor = LoadCursor(NULL, IDC_ARROW); /* Кисть фона и ее цвет можно определить выражениями: wcex.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE+1); wcex.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH); или с помощью макроса GetStockBrush(), в этом случае необходимо подключить файл windowsx.h */ wcex.hbrBackground=GetStockBrush(LTGRAY_BRUSH); /*wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MSG_1);*/ /* Имя класса главного окна */ wcex.lpszClassName = szWindowClass; /* Маленькая пиктограмма, wcex.hIconSm, которую можно загрузить функцией LoadImage() */ wcex.hIconSm = NULL; if(!RegisterClassEx(&wcex)) return FALSE; /*Создаем главное окно*/ hWnd = CreateWindowEx(WS_EX_WINDOWEDGE,szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,0,CW_USEDEFAULT,0, NULL, NULL, hInstance, NULL); if (!hWnd) return FALSE; /* Исследуем адресное пространство приложения. Выведем со- держимое сегментных регистров команд, данных и стека, а также смещение главной функции и строки с именем класса */ TCHAR szAsm[80]; USHORT regCS,regDS,regES,regSS; __asm{ mov regCS,CS mov regDS,DS mov regES,ES mov regSS,SS } wsprintf((LPTSTR)szAsm,_T("CS=%X,DS=%X\nES=%X,SS=%X\n WinMain=%X\nszWindowClass=%X"), regCS,regDS,regES,regSS); MessageBox(NULL,(LPCTSTR)szAsm,_T("Регистры"), MB_ICONINFORMATION); /*Делаем окно видимым на экране*/ ShowWindow(hWnd, SW_SHOWNORMAL); /* Функция UpdateWindow() вызывает передачу сообщения WM_PAINT непосредственно оконной процедуре, а функция InvalidateRect()вызывает постановку сообщения WM_PAINT в очередь приложения, а там оно обра- батывается с самым низким приоритетом 36 */ UpdateWindow(hWnd); /*Цикл обработки сообщений*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } /*Код возврата*/ return (int)msg.wParam; } /*Оконная функция главного окна*/ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { /*Горизонтальный размер главного окна*/ int xSize=500; /*Верикальный размер главного окна*/ int ySize=300; /*Структура PAINTSTRUCT с характеристиками рабочей области,заполняется функцией BeginPaint */ PAINTSTRUCT ps; /* TEXTMETRIC - структура для получения характеристик шрифта */ TEXTMETRIC tm; /* LOGFONT - структура для для создания логических шрифтов */ LOGFONT lf; static HFONT hFont, hOldFont; /* RECT - структура описывает прямоугольник */ RECT rect; LPMINMAXINFO lpmmi; /*Дескриптор контекста устройства*/ HDC hDc; UINT width, height; /*Имя шрифта*/ LPTSTR lpszFace=_TEXT("Times New Roman Cyr"); switch (message) { /*Переход по значению кода сообщения(msg)*/ case WM_CREATE: /* Только здесь можно произвести модификацию класса окна Например , SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(200,160,255)); Значение дескриптор экземпляра приложения (hInstance) определяется, вызовом одной из следующих функций: hInst = GetModuleHandle(NULL); hInst = (HINSTANCE)GetClassLong(hWnd,GCL_HMODULE); */ /*Обнуление всех членов структуры lf*/ memset(&lf,0,sizeof(lf)); /*Устанавливаем размер шрифта*/ 37 lf.lfHeight=30; /*Копируем в структуру имя шрифта*/ lstrcpy(lf.lfFaceName,lpszFace); /*Создаем шрифт*/ hFont = CreateFontIndirect(&lf); /*Первый немедленный вывод текущего времени*/ OutTimeDate(hWnd); /* Функция SetTimer создает системный таймер с периодом 1с */ SetTimer(hWnd,1,1000,(TIMERPROC)NULL); return TRUE; case WM_TIMER: /* Функция OutTimeDate запрашивает у системы текущие значе- ния даты и времени, а затем организует их обработку в главном окне приложения. */ OutTimeDate(hWnd); break; case WM_KEYDOWN: /*Обрабатываем сообщение-нажатие клавиши.*/ switch(wParam) { case VK_ESCAPE: /* Посылаем сообщение WM_CLOSE окну (hWnd), а после того, как оконная процедура обработает это сооб- щение, система передаст управление инструкции следующей за SendMessage. */ SendMessage(hWnd,WM_CLOSE,0,0); break; } break; case WM_COMMAND: switch(LOWORD(wParam)) //switch(wParam) { case ID_FILE_TEST: //… break; } break; case WM_PAINT: /*Получаем контекст устройства*/ hDc = BeginPaint(hWnd, &ps); /*Определяем размеры клиентской области окна*/ GetClientRect(hWnd,&rect); /*Выбираем в контест созданный шрифт*/ hOldFont=SelectFont(hDc,hFont); /*Получим метрики текста*/ GetTextMetrics(hDc,&tm); /* Функция SetBkMode устанавливает текущий режим фона. TRANSPARENT - в этом режиме вывода текста цвет фона графического элемента игнорируется, т.е. символ выводится на существующем фоне. */ 38 SetBkMode(hDc,TRANSPARENT); /* Функция SetTextColor устанавливает цвет текста для контекста устройства, по умолчанию применяется черный цвет. Цвет текста синий! */ SetTextColor(hDc,RGB(0,0,128)); DrawText(hDc,szCurrentTime,-1,&rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); /*Освобождаем контекст устройства*/ EndPaint(hWnd, &ps); break; case WM_CLOSE: /* Сообщение WM_CLOSE появляется при щелчке на кнопке закрытия окна - здесь предназначено для вывода преду- преждающего сообщения */ if(MessageBox(hWnd,_T("Вы уверены?"), _T("Bad boys"), MB_YESNO | MB_ICONQUESTION)==IDYES) { /* Функция DestroyWindow разрушает указанное в ее па- раметре окно, т.е. она посылает окну сообщение WM_DESTROY. Затем вызывается функция PostQuitMessage, которя посылает сообщение WM_QUIT */ DestroyWindow(hWnd); } break; case WM_SIZE: /* Ширина width и высота height клиентской области окна в пикселях */ width=LOWORD(lParam); height=HIWORD(lParam); //... break; case WM_LBUTTONDOWN: /*Нажата клавиша Shift ?*/ if(wParam & MK_SHIFT) { MessageBox(hWnd,_T("Нажата клавиша\nShift"), _T("Сообщение!"), MB_OK|MB_ICONEXCLAMATION); } break; case WM_GETMINMAXINFO: lpmmi=(LPMINMAXINFO)lParam; lpmmi->ptMinTrackSize.x=xSize; lpmmi->ptMinTrackSize.y=ySize; lpmmi->ptMaxTrackSize.x=xSize; lpmmi->ptMaxTrackSize.y=ySize; break; case WM_DESTROY: /* Функция DeleteObject удаляет логический объект. 39 К удаляемым объектам относятся перья, растровые изображения, кисти, области, палитры и шрифты. */ /*Удаляем созданный шрифт*/ DeleteObject(hFont); /*Функция KillTimer удаляет таймер*/ KillTimer(hWnd,1); /*PostQuitMessage() выполняет только одно действие - ставит в очередь сообщение WM_QUIT. Параметр у этой функции - код возврата, который помещается в wParam */ PostQuitMessage(0); break; default: /*Обработка прочих сообщений по умолчанию*/ return DefWindowProc(hWnd, message,wParam, lParam); } return 0L; } /* Фунуция получения текущего времени и преобразование его в символы */ void OutTimeDate(HWND hWnd) { LPTSTR szDay[]={_T("Вск."),_T("Пнд."),_T("Втр."), _T("Ср."),_T("Чтв."), _T("Птн."),_T("Суб.") }; LPTSTR szMonth[]={_T(""),_T("Янв."),_T("Февр."), _T("Март"),_T("Апр."), _T("Май"),_T("Июнь"), _T("Июль"),_T("Авг."), _T("Сент."),_T("Окт."), _T("Нояб."),_T("Дек.") }; TCHAR szT[20]; SYSTEMTIME SystemTime; /* Функция GetLocalTime осуществляет выборку местного време- ни, на которое настроен компьютер, т.е. функция заполняет структуру типа SYSTEMTIME в числовом виде. */ GetLocalTime(&SystemTime); /*День недели*/ lstrcpy(szCurrentTime, szDay[SystemTime.wDayOfWeek]); /*Разделяющий пробел*/ lstrcat((LPTSTR)szCurrentTime,_T(" ")); /*Месяц*/ lstrcat((LPTSTR)szCurrentTime, szMonth[SystemTime.wMonth]); /*Разделяющий пробел*/ lstrcat((LPTSTR)szCurrentTime,_T(" ")); /*Дату переводим в символы*/ 40 wsprintf((LPTSTR)szT,_T("%d"), SystemTime.wDay); lstrcat((LPTSTR)szCurrentTime,(LPTSTR)szT); /*Разделяющий пробел*/ lstrcat((LPTSTR)szCurrentTime,_T(" ")); /*Год переводим в символы*/ wsprintf((LPTSTR)szT,_T("%d"), SystemTime.wYear); lstrcat((LPTSTR)szCurrentTime,(LPTSTR)szT); lstrcat((LPTSTR)szCurrentTime,_T("---")); /**Часы переводим в символы*/ wsprintf((LPTSTR)szT,_T("%d"), SystemTime.wHour); lstrcat((LPTSTR)szCurrentTime,(LPTSTR)szT); /*Разделяющее двоеточие*/ lstrcat((LPTSTR)szCurrentTime,_T(":")); /*Минуты переводим в символы*/ wsprintf((LPTSTR)szT,_T("%d"), SystemTime.wMinute); lstrcat((LPTSTR)szCurrentTime,(LPTSTR)szT); /*Разделяющее двоеточие*/ lstrcat((LPTSTR)szCurrentTime,_T(":")); /*Сеуцнды переводим в символы*/ wsprintf((LPTSTR)szT,_T("%d"), SystemTime.wSecond); lstrcat((LPTSTR)szCurrentTime,(LPTSTR)szT); /*Перерисовка окна*/ InvalidateRect(hWnd,NULL,TRUE); } Результат работы программы представлен на рис. 2. Рис. 2. Окно программы 2. МЕНЮ 2.1. Файл ресурсов В файле ресурсов описываются форматы меню и диалоговых окон, растровые изображения, пиктограммы, курсоры, таблицы сим- вольных строк, шрифты и т.д. 41 Файл ресурсов является текстовым файлом и имеет стандартное расширение .RC. Файл ресурсов обрабатывается компилятором ресурсов, получая из него промежуточный файл с расширением .RES. Далее компонов- щик компонует файлы .OBJ и .RES в единый загрузочный файл с рас- ширением .EXE. Элементы приложения, описанные в файле ресурсов сосредото- чены в загрузочном файле в одном месте и хранятся в определенном формате. Это дает возможность специальным программам читать или даже модифицировать ресурсы непосредственно в загрузочном файле приложения. 2.2. Организация и виды меню Меню является важнейшим элементом большинства Windows- приложений. Под строкой заголовка главного окна отображается полоса меню (menu bar), содержащая набор пунктов. Такое меню называется главным (main menu) или меню верхнего уровня, которое относится ко всему приложению. Любое меню содержит пункты меню, которые обозначаются своими именами. Имя пункта может содержать выделенный подчеркиванием сим- вол, который называется мнемоническим символом. Мнемонический сим- вол определяет горячую клавишу (hotkey), служащую для выбора пункта меню при помощи клавиатуры. Различают два типа пунктов меню: 1) пункт-команду – терминальный (конечный) пункт на иерархиче- ском дереве меню, которому в программном коде соответствует уникаль- ный целочисленный идентификатор; 2) пункт-подменю – заголовок вызываемого всплывающего меню (po- pup menu) следующего, более низкого уровня. Реакция системы на выбор пункта зависит от типа пункта: • выбран пункт-команда – система посылает приложению сообщение WM_COMMAND, которое содержит идентификатор этой команды, т.е. выбор этого пункта заставляет приложение выполнить некоторое действие; • выбран пункт-подменю – система выводит на экран подменю, т.е. всплывающее меню (если пункт подменю содержит мно- готочие, то при выборе данного пункта выводится диалоговое окно). Пункты меню могут быть разрешенными (enabled), запрещенны- ми (disabled) или недоступными (grayed). Запрещенный и недоступ- ный пункты меню по своему поведению одинаковы и различаются только внешним видом. Запрещенный пункт (disabled) выглядит так же, как и разрешенный (enabled), а недоступный (grayed) изображается серым 42 цветом. Следовательно, отмененный пункт меню для большей информа- тивности должен быть определен как недоступный (grayed). Для изменения статуса пунктов меню применяется функция EnableMenuItem(), ее прототип: BOOL EnableMenuItem( HMENU hMenu, //дескриптор меню UINT uIDEnableItem, //идентификатор или позиция пункта UINT uEnable //интерпретация второго параметра и //выполняемое действие ); Функция EnableMenuItem() может применяться для пунктов, как главного меню, так и подменю. Пункт меню, для которого применяется функция, задается вторым параметром uIDEnableItem. Этому параметру передается либо иден- тификатор пункта меню в файле описания ресурсов, либо позиция пункта меню. Интерпретация этого параметра определяется третьим параметром − uEnable. Параметр uEnable должен определяться комбинацией из двух флагов. Первый флаг может принимать одно из двух значений: • MF_BUCOMMAND – второй параметр uIDEnableItem функции со- держит идентификатор пункта меню; • MF_BYPOSITION – второй параметр uIDEnableItem функции со- держит позицию пункта меню, отсчитываемую от нуля. Второй флаг может принимать одно из трех значений: • MF_ENABLED – пункт разрешен; • MF_DISABLED – пункт запрещен; • MF_GRAYED – пункт недоступен. Например: MF_BYCOMMAND | MF_GRAYED. Если в результате применения функции EnableMenuItem() из- меняется статус пункта главного меню, то следует обязательно применить функцию DrawMenuBar() для перерисовки изменившейся полосы ме- ню. Прототип функции DrawMenuBar(): BOOL DrawMenuBar(HWND hWnd ); Пункты меню могут использоваться в роли флажков (check box). Флажок может быть установлен (символ «галочка») или сброшен. Пе- реход из одного состояния в другое происходит при каждом выборе пунк- та меню. Флажки объединяются в группы и обеспечивают при этом вы- бор одной или нескольких опций одновременно. Для управления выбором пунктов-флажков (ставит или снимает отметку на пункте меню) используется функция CheckMenuItem(), ее прототип: DWORD ChecMenuItem( HMENU hMenu, //дескриптор меню UINT uIDCheckItem, //идентификатор или позиция пункта меню UINT uCheck //интерпретация второго параметра и //выполняемое действие ); 43 Функция ChecMenuItem() может применяться только для пунк- тов подменю, а пункты главного меню не могут быть поме- чены. Функция возвращает предыдущее состояние пункта или −1. Пункт подменю, для которого применяется функция, задается вто- рым параметром uIDCheckItem. Этому параметру передается либо идентификатор пункта меню в файле описания ресурсов, либо позиция пункта меню. Интерпретация этого параметра определяется третьим па- раметром − uCheck. Параметр uCheck задается комбинацией двух флагов. Первый флаг может принимать одно из двух значений: • MF_BUCOMMAND – второй параметр uIDCheckItem функции содер- жит идентификатор пункта меню; • MF_BYPOSITION – второй параметр uIDCheckItem функции содер- жит позицию пункта меню, отсчитываемую от нуля. Второй флаг может принимать одно из двух значений: • MF_CHECKED – поместить отметку слева от пункта подменю; • MF_UNCHECKED – снять отметку слева от пункта подменю. Например: MF_BYCOMMAND|MF_CHECKED. Наиболее предпоч- тительно в комбинации всегда использовать флаг MF_BYCOMMAND. Пункты меню могут использоваться в роли переключателей (ra- dio button), которые, как и флажки, обычно используются в группе. Переключатели связываются только с взаимоисключающими опциями. Следовательно, в группе можно выбрать только один переключатель. При этом он отмечается точкой или закрашенным кружком. Для управления выбором пунктов-переключателей (ставит отметку на выбранном пункте и снимает со всех остальных пунктов в указанной группе) используется функция CheckMenuRadioItem(), ее прототип: BOOL CheckMenuRadioItem( HMENU hMenu, //дескриптор меню UINT idFirst, //идентификатор или позиция первого //пункта в группе UINT idLast, //идентификатор или позиция //последнего пункта в группе UINT idCheck, //идентификатор или позиция //выбранного пункта в группе UINT uFlags //интерпретация параметров idFirst, //idLast и idCheck ); Функция CheckMenuRadioItem() может применяться только для пунктов подменю, а пункты главного меню не могут быть по- мечены . Функция возвращает предыдущее состояние пункта или −1. Пункт подменю, выбранный из группы пунктов, задается четвер- тым параметром idCheck. Параметрам idFirst, idLast и idCheck передается либо идентификатор пункта меню в файле описания ресурсов, либо позиция пункта меню. Интерпретация этих параметров определяется пятым параметром uFlags, который может принимать следующие зна- чения: 44 • MF_BYCOMMAND – параметрам со второго по четвертый передаются идентификаторы соответствующих пунктов меню; • MF_BYPOSITION – параметрам со второго по четвертый передаются позиции соответствующих пунктов меню. Задавая пункты меню для группы переключателей, следует убедиться, что их идентификаторы в файле resource.h имеют сквозную нумерацию и упорядочены в соответст- вии с позициями этих пунктов на полосе меню. Подменю может содержать пункт, который выполняется по умол- чанию. Имя этого пункта выделяется жирным шрифтом. Если подменю содержит пункт по умолчанию, то при открытии подменю двойным щелчком мыши Windows автоматически выполнит соответствующую ко- манду. Функцией SetMenuDefaultItem() может быть назначен лю- бому пункту подменю так называемый атрибут «по умолчанию», ее про- тотип: BOOL SetMenuDefaultItem( HMENU hMenu , UINT uItem , UINT fByPos ); Для получения дескриптора меню (hMenu) верхнего уровня приме- няется функция GetMenu(), а для получения дескриптора подменю – функция GetSubMenu(). Они имеют следующие прототипы: HMENU GetMenu(HWND hWnd); HMENU GetSubMenu( HWND hWnd, //дескриптор родительского меню int nPos //позиция пункта–подменю в роди- //тельском меню ); Функция GetMenu() возвращает NULL, если окно hWnd не имеет меню. Позиция пункта родительского меню nPos отсчитывается от нуля. Если пункт с позицией nPos не активизирует всплывающее меню, а яв- ляется пунктом-командой, то функция возвращает значение NULL. Функ- цию GetMenu() нельзя применять для дочерних окон. Меню можно создать следующими способами: • на основе шаблона меню, определенного в файле ресурсов (в глав- ном меню Visual Studio выполнить команды Project -> Add Resourse -> Menu , в результате будет открыто окно редакто- ра меню); • динамическим способом, т.е. непосредственно в приложение при помощи функций CreateMenu(), AppendMenu(), Create- PopupMenu() и SetMenu(); • альтернативным способом при помощи функции LoadMenuIndi- rect(), которая считывает определение меню в блок памяти и возвращает дескриптор созданного меню (подключение меню к ок- ну – SetMenu()). 45 Прототип функции LoadMenuIndirect(): HMENU LoadMenuIndirect(CONST MENUTEMPLATE * lpMenuTemplate); Чтобы меню, определенное в файле ресурсов, появилось в составе приложения, необходимо присвоить значение указателя на имя меню по- лю lpszMenuName структурной переменной типа WNDCLASSEX. В слу- чае, когда имя меню определено как целочисленный идентификатор (IDR_MENU1), его необходимо преобразовать в указатель на строку ре- сурса, используя макрос MAKEINTRESOURCE, например: wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1); В обоих случаях при создании окна с помощью функции CreateWindowEx() ее параметру hMenu следует передать значение NULL. Таким образом, присоединенное меню будет использоваться по умолчанию всеми окнами данного класса. Для связывания с окном приложения другого меню, т.е. не меню, используемого по умолчанию, необходимо выполнить следующие дейст- вия: 1) загрузить при помощи функции LoadMenu() требуемое меню; 2) определить требуемое меню с помощью функции SetMenu(), пе- редав ей в качестве второго параметра, дескриптор меню (hMenu), воз- вращенный функцией LoadMenu() (при этом новое меню заменяет ста- рое меню, если оно уже было). Функции LoadMenu() и SetMenu() имеют следующие прототи- пы: HMENU LoadMenu(HINSTANCE hInstance,LPCTSTR lpMenuName); BOLL SetMenu(HWND hWnd, HMENU hMenu); На основании вышеизложенного создадим меню динамически, т.е. непосредственно в приложении (листинг 2.3). Необходимо иметь в виду строгую древовидную структуру меню, которая начинается с верхнего уровня. К меню верхнего уровня могут быть присоединены как конечные элементы меню, так и элементы, выбор которых приводит к появлению всплывающих (popup) меню. В свою очередь к всплывающим меню при- соединяются элементы очередного уровня и т.д. Таким образом, алгоритм создания меню в приложении следующий: 1) выбрать подменю самого низкого уровня, которое содержит только конечные элементы меню, и создать их с помощью одной из функций CreateMenu() или CreatePopupMenu(); 2) добавить функцией AppendMenu() в созданное пустое меню тре- буемые пункты меню; 3) создать меню следующего, более высокого уровня, и добавить в них пункты меню и меню, созданные на предыдущем шаге; 4) повторять пп. 1,2 и 3 до тех пор, пока не будут созданы все подме- ню; 5) создать главное меню программы функцией CreateMenu(); 6) присоединить созданные подменю самого высокого уровня к глав- ному меню программы функцией AppendMenu(); 7) присоединить меню к окну функцией SetMenu(); 46 8) прорисовать меню функцией DrawMenuBar(), так как строка ме- ню не является частью клиентской области и, следовательно, не обновля- ется при вызове функции UpdateWindow(). Прототипы функций DrawMenuBar(), CreateMenu() и CreatePopupMenu() соответственно: BOOL DrawMenuBar(HWND hWnd); HMENU CreateMenu(VOID); HMENU CreatePopupMenu(VOID); Функция AppendMenu() добавляет новый элемент в конец меню. Всплывающими меню считаются все меню, кроме верхней строки. BOOL AppendMenu( HMENU hMenu, //дескриптор меню, к которому //добавляется новый пункт UINT uFlags //вид и правило поведения //добавляемого пункта меню UINT idNewItem, //идентификатор для нового //пункта меню LPCTSTR lpszNewItem //содержимое нового пункта //меню (зависит от uFlags) ); Значения параметра uFlags (полный список в MSDN): • MF_POPUP – создается всплывающее меню; • MFS_CHECKED – помещается отметка рядом с пунктом меню; • MFS_DEFAULT – пункт меню, применяемый по умолчанию; • MFS_GRAYED – пункт меню выделяется серым цветом и запре- щается его выбор; • MFS_HILITE – пункт меню высвечивается; • MFT_STRING – пункт меню отображается с использованием текстовой строки. Функция DestroyMenu() уничтожает меню, созданное с приме- нением файла ресурса, или меню, созданное в теле программы с помо- щью функций CreateMenu() и CreatePopupMenu(). По завершении приложения будет уничтожено только одно меню, подключенное к окну приложения. Меню, не подключенные в данный момент к окну, останутся в памяти, поэтому функция DestroyMenu() должна обязательно при- меняться в приложениях с несколькими меню. Прототип функции DestroyMenu(): BOOL DestroyMenu(HMENU hMenu); |