Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows

.pdf
Скачиваний:
0
Добавлен:
11.02.2026
Размер:
13.15 Mб
Скачать

Элементы управления главного окна

411

 

 

 

// Функция отладочной печати (аналог макроса TRACE в библиотеке MFC)

#define N 512

void TRACE(LPCTSTR szFormat, ...)

{

va_list args; va_start(args, szFormat); int nBuf;

char szBuffer[N];

nBuf = _vsnprintf(szBuffer, N, szFormat, args);

if (nBuf < 0) {

MessageBox(NULL, "Слишком длинная строка для TRACE!", "Ошибка", MB_OK | MB_ICONSTOP);

szBuffer[N-2] = '\n'; szBuffer[N-1] = 0;

}

OutputDebugString(szBuffer);

va_end(args);

}

//////////////////////////////////////////////////////////////////////

// ToolTip.cpp #include <windows.h> #include <commctrl.h> #include <stdio.h> #include "KWndEx.h" #include "resource.h"

/* Определения макросов, типов и глобальных переменных из листинга 8.3 */

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

{

MSG msg;

KWndEx mainWnd("ToolTip", hInstance, nCmdShow, WndProc, MAKEINTRESOURCE(IDR_MENU1), 100, 100, 400, 300);

// Добавление окна подсказки AddTooltip (mainWnd.GetHWnd(),

"Это клиентская область главного окна приложения");

while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg);

}

return msg.wParam;

}

//====================================================================

продолжение

412

Глава 8. Элементы управления общего пользования

 

 

Листинг 8.4 (продолжение)

HWND InitToolBar(HWND hWnd) {

/* Здесь такой же текст, как и в листинге 8.3 */

// Добавление окон подсказки

AddTooltip (hwndCombo, MAKEINTRESOURCE(IDS_TB_COMBOBOX)); HWND hwndEdit = GetWindow(hwndCombo, GW_CHILD);

AddTooltip (hwndEdit, MAKEINTRESOURCE(IDS_EDIT_IN_COMBO)); return hToolBar;

}

//==================================================================== void UpdateToolBar(ShapeData& sd) {

/* Здесь такой же текст, как и в листинге 8.3 */

}

//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

/* Здесь такой же текст, как и в листинге 8.3, за исключением фрагмента: case IDM_ABOUT:

MessageBox(hWnd,

"ToolTip\nVersion 1.0\nCopyright: Finesoft Corporation, 2005.", "About ToolTip", MB_OK);

break;

*/

}

//////////////////////////////////////////////////////////////////////

Обратите внимание на следующие особенности программы:

Файлы KWndEx.h и KWndEx.cpp содержат интерфейс и реализацию класса KWndEx, а также интерфейс и реализацию функций общего применения.

Класс KWndEx отличается от класса KWnd только реализацией конструктора. В новом классе добавлен вызов функции InitCommonControlsEx, регистрирую щей оконные классы элементов управления общего пользования.

Функция AddTooltip, имеющая следующий прототип:

void AddTooltip (HWND hwndOwner, LPTSTR lpMsg);

предназначена для добавления элемента управления Tooltip к окну hwndOwner. Второму параметру функции передается либо указатель на С строку, либо иден тификатор строкового ресурса в таблице строк приложения.

Хотя элемент управления Tooltip допускает регистрацию нескольких инстру ментов для окна hwndOwner, для упрощения реализации функция AddTooltip ре гистрирует только один инструмент, интерпретируемый как вся клиентская область окна hwndOwner.

Первый раз функция AddTooltip вызывается в теле функции WinMain:

AddTooltip (mainWnd.GetHWnd(),

"Это клиентская область главного окна приложения");

Второй и третий вызовы включены в текст функции InitToolBar:

AddTooltip (hwndCombo, MAKEINTRESOURCE(IDS_TB_COMBOBOX)); HWND hwndEdit = GetWindow(hwndCombo, GW_CHILD); AddTooltip (hwndEdit, MAKEINTRESOURCE(IDS_EDIT_IN_COMBO));

Элементы управления главного окна

413

 

 

 

Обратите внимание на использованный способ получения дескриптора окна редактирования, входящего в состав комбинированного списка.

Функция TRACE, имеющая следующий прототип:

void TRACE(LPCTSTR szFormat, ...)

предназначена для отладочного вывода в окно Output. Функция принимает пе ременное количество аргументов, и обращение к ней похоже на обращение к функции printf из библиотеки С. Реализация функции основана на использо вании функции OutputDebugString.

Программисты, написавшие хотя бы один проект с библиотекой MFC, сразу оце нивают удобство использования макроса TRACE в режиме отладки. К сожалению, Win32 API этот макрос не поддерживает. Но теперь, подключив к любому проекту файлы KwndEx.h и KwndEx.cpp, вы не будете чувствовать себя ущемленными!

Чтобы использовать отладочную печать, не забудьте выполнить два условия. Во первых, проект должен компилироваться в отладочной конфигурации (Win32 Debug). Во вторых, программа должна запускаться на выполнение в режиме от ладки при помощи команды меню Start Debug Go или клавиши F5.

Откомпилируйте проект и проверьте, как работает подсказка для комбиниро ванного списка в панели инструментов и для главного окна приложения.

Проверьте также работу функции отладочного вывода TRACE. В блок обработ ки сообщения WM_NOTIFY можно добавить следующую инструкцию:

TRACE("uMsg = %04X, wParam = %08X, lParam = %08X\n", uMsg, wParam, lParam);

Теперь, если запустить программу в режиме отладки, благодаря функции TRACE в окно Output будут выводиться значения uMsg, wParam и lParam при каждом по ступлении в оконную процедуру сообщения WM_NOTIFY. Отладочная печать будет выглядеть примерно так:

uMsg = 004E, wParam = 000000C9, lParam = 0012F768 uMsg = 004E, wParam = 00009C48, lParam = 00135AC0 uMsg = 004E, wParam = 00009C48, lParam = 0012FA34

. . . . . . . . . . . . . . . . . . . . . . . . .

Строка состояния

Строка состояния (status bar) — это окно, обычно располагающееся в нижней части главного окна приложения и предназначенное для информирования поль зователя о текущем состоянии программы, о выполняемых операциях и режи мах. Довольно часто строка состояния используется для отображения подроб ного описания пунктов меню при наведении на них курсора мыши. Когда меню не просматривается, приложение может выводить другую справочную инфор мацию.

Для создания строки состояния следует использовать функцию CreateStatusWindow:

hwndStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", hWndParent, ID_STATUS_BAR);

В процессе своего выполнения эта функция вызывает функцию CreateWindow, которая создает дочернее окно с родительским окном hWndParent, идентификато ром ID_STATUS_BAR и выводит в окно строки состояния текст, заданный вторым параметром. В данном примере будет использоваться пустая строка.

414

Глава 8. Элементы управления общего пользования

 

 

По умолчанию окно строки состояния имеет дополнительные стили CCS_BOTTOM (см. табл. 8.7) и SBARS_SIZEGRIP. Стиль SBARS_SIZEGRIP задает наличие «манипу лятора размера» в правом углу строки состояния. Этот декоративный элемент соз дает область, за которую можно ухватиться при изменении размера окна прило жения.

Строка состояния может работать в двух режимах:

Стандартный, или многочастный, режим (Multiple Part Status Bars), в кото ром строка состояния разбивается на несколько частей (полей). В каждом поле выводится отдельная строка текста.

Простой режим (Simple Mode Status Bars), в котором строка состояния реали зована как единый элемент и отображает только одну строку. Простой режим чаще всего используется при отображении справочной информации для пунк тов меню, когда пользователь перемещается по меню.

Переключение между режимами осуществляется посылкой сообщения SB_SIMPLE:

SendMessage(hwndStatusBar, SB_SIMPLE, fMode, 0);

Когда параметр fMode имеет значение TRUE, то устанавливается простой режим. При использовании значения FALSE строка состояния будет работать в стандарт ном (многочастном) режиме.

Разделение строки состояния на поля

Если строка состояния используется в стандартном режиме, то вы должны разде лить ее на отдельные поля при помощи отсылки сообщения SB_SETPARTS:

SendMessage(hwndStatusBar, SB_SETPARTS, nParts, (LPARAM)aWidths);

Здесь параметр nParts задает число полей, а aWidths — адрес целочисленного массива, каждый элемент которого определяет позицию (в клиентских координа тах) правой границы соответствующего поля. Если элемент массива aWidths равен –1, то границей соответствующего поля считается правая граница строки состоя ния.

Обычно в каждое поле строки состояния выводится отдельное текстовое сооб щение. Но вы можете также разместить в любом поле другой элемент управле ния, например индикатор процесса (Progress bar). При таком размещении полезно знать клиентские координаты этого поля, которые могут быть получены при по мощи отправки сообщения SB_GETRECT:

SendMessage(hwndStatusBar, SB_GETRECT, iPart, (LPARAM)&rect);

где iPart — номер поля, отсчитываемый от нуля, а rect — структура типа RECT, при нимающая координаты поля.

Вывод текстового сообщения

Для отображения текста в строке состояния посылается сообщение SB_SETTEXT:

SendMessage(hwndStatusBar, SB_SETTEXT, wParam, (LPARAM)szText);

где szText — указатель на C строку, а wParam задает номер поля в строке состояния и графический стиль для этого поля.

Значение параметра wParam может быть задано в виде объединения iPart | uType. Если строка состояния работает в стандартном режиме, то iPart содержит но мер поля. Если используется простой режим, то параметр iPart должен иметь зна

чение 255.

Элементы управления главного окна

415

 

 

 

Флаг uType может иметь одно из следующих значений:

Значение флага uType

Интерпретация

 

 

0 (значение по умолчанию)

Поле рисуется с вдавленной рамкой

SBT_NOBORDERS

Поле рисуется без рамки

SBT_POPOUT

Поле рисуется с выпуклой рамкой

SBT_OWNERDRAW

За прорисовку поля отвечает родительское окно

 

 

Максимальная длина строки, выводимой в каждом поле, составляет 127 сим волов.

Размеры и позиция строки состояния

Оконная процедура дочернего окна строки состояния автоматически устанавли вает начальную позицию и размеры этого элемента управления. Ширина строки состояния равна ширине клиентской области родительского окна. Высота строки состояния устанавливается на основе метрик шрифта, выбранного по умолчанию

вконтекст устройства элемента управления.

Вдальнейшем оконная процедура строки состояния автоматически регулирует ее позицию и ширину всякий раз, когда получает сообщение WM_SIZE. Из этого выте кает, что при каждом изменении размеров родительского окна, то есть при получе нии сообщения WM_SIZE, оконная процедура WndProc должна отправить строке состо яния такое же сообщение, передав текущие значения параметров wParam и lParam:

SendMessage (hwndStatusBar, WM_SIZE, wParam, lParam);

Если высота строки состояния по умолчанию вас не устраивает, вы можете ее изменить, послав сообщение SB_SETMINHEIGHT:

SendMessage(hwndStatusBar, SB_SETMINHEIGHT, minHeight, 0);

где minHeight — минимальная высота окна строки состояния в пикселах. На самом деле Windows устанавливает высоту окна строки состояния, равной minHeight + + 2*wVB, где wVB — ширина вертикальной рамки окна.

Возможность увеличить высоту строки состояния бывает полезной, если на ней размещены другие дочерние окна элементов управления (см. ниже приложе ние ProgressBar).

Уменьшение видимой части клиентской области главного окна

Так же как и панель инструментов, строка состояния занимает часть клиентской области главного окна приложения. К сожалению, Windows не корректирует ав томатически размеры и начало координат клиентской области при появлении панели инструментов и строки состояния. В приложении ToolBar мы вычисляли прямоугольник, ограничивающий видимую часть клиентской области, с учетом высоты панели инструментов tbHeight. Точно так же при использовании строки состояния необходимо учитывать уменьшение высоты видимой части клиентс кой области на величину, равную высоте строки состояния.

Поддержка просмотра меню

Пользователи, работающие с Windows программами, привыкли, что в строке со стояния отображается информация о назначении выбранного ими пункта меню. Меню посылает сообщение WM_MENUSELECT, когда пользователь осуществляет

416

Глава 8. Элементы управления общего пользования

 

 

навигацию по его пунктам с помощью мыши или клавиатуры. Поэтому для поддерж ки отображения справочной информации необходимо обрабатывать это сооб щение.

Когда оконная процедура получает сообщение WM_MENUSELECT, из его парамет ров можно извлечь следующую информацию:

Младшее слово параметра wParam содержит идентификатор пункта меню, если это пункт команда, или индекс подменю (если это пункт подменю).

Старшее слово параметра wParam содержит один из флагов, характеризующих статус выбранного пункта меню. Из всех возможных флагов сейчас нас будет интересовать только флаг MF_POPUP, означающий, что данный пункт открыва ет всплывающее меню или подменю.

Параметр lParam содержит дескриптор меню, которому принадлежит выбран ный пункт.

Win32 API содержит функцию MenuHelp, предназначенную для упрощения об работки сообщений WM_MENUSELECT и отображения текста в строке состояния. Тех нология использования этой функции описана в [1]. Однако, на мой взгляд, эта технология довольно громоздка и неудобна. Здесь предлагается альтернативная технология, в которой функция MenuHelp не применяется.

Ранее мы уже убедились, что бывает очень удобно использовать ресурс табли цы строк при отображении окон подсказок для кнопок панели инструментов. Простота решения базировалась на совпадении идентификаторов кнопок и иден тификаторов строк.

Хотелось бы использовать аналогичную идею и для решения рассматривае мой проблемы. Однако если пункты команды в меню имеют уникальные иденти фикаторы и мы можем использовать эти же идентификаторы для строк в таблице строк, то с пунктами подменю ситуация гораздо сложней. В пределах меню каж дого уровня пункты подменю нумеруются начиная с нуля, поэтому ни о какой уникальности не может быть и речи.

Идея решения, предлагаемая здесь, базируется на некоторой договоренности о системе идентификации таких пунктов меню. Сразу уточним, что мы рассмат риваем только те меню, которые имеют не более трех уровней. Нулевой уровень — это меню верхнего уровня, подменю в нем имеет первый уровень, а подменю в меню первого уровня получает второй уровень. Большинство Windows программ удовлетворяет этому ограничению.

Процедура однозначной идентификации пунктов-подменю

Для пунктов подменю, принадлежащих меню нулевого уровня, в качестве иден тификаторов используются индексы, присваиваемые им Windows, то есть 0, 1, 2 и т. д. Эти значения содержатся в младшем слове параметра wParam сообщения

WM_MENUSELECT.

Для пунктов подменю, принадлежащих некоторому меню первого уровня, уни кальный идентификатор формируется на базе индекса itemID, извлекаемого из младшего слова параметра wParam, посредством добавления соответствующего смещения:

itemID += 100 * (submenuID + 1);

где submenuID — индекс рассматриваемого подменю в меню нулевого уровня.

Элементы управления главного окна

417

 

 

 

Таким образом, для меню первого уровня с нулевым индексом (submenuID=0) входящие в него пункты подменю получат номера 100, 101, 102 и т. д. Для меню первого уровня с единичным индексом входящие в него пункты подменю полу чат номера 200, 201, 202 и т. д. Такие же идентификаторы мы будем использовать и для строк в таблице строк приложения.

Из принятого нами ограничения на количество уровней меню вытекает, что все пункты второго (последнего) уровня являются пунктами командами и поэто му имеют уникальные идентификаторы.

Получив в результате обработки сообщения WM_MENUSELECT уникальный иден тификатор пункта меню itemID, мы можем извлечь из таблицы строк соответству ющую символьную строку в текстовый буфер text, а затем отобразить этот текст в строке состояния, которая работает в простом режиме. Это может быть реализо вано следующими инструкциями:

SendMessage(hwndStatusBar, SB_SIMPLE, TRUE, 0); LoadString(GetModuleHandle(NULL), itemID, text, 200); SendMessage(hwndStatusBar, SB_SETTEXT, 255, (LPARAM)text);

Для демонстрации предложенной технологии разработаем приложение StatusBar, являющееся модификацией приложения ToolBar. В результате доработки к про грамме будет добавлена строка состояния, работающая в простом и стандартном режимах.

Приложение StatusBar

Создайте новый проект с именем StatusBar. Скопируйте из папки проекта ToolBar

(см. листинг 8.1) в папку проекта StatusBar файлы ToolBar.cpp, ToolBar.rc и resource.h, скорректировав имена первых двух файлов заменой подстроки ToolBar на StatusBar. Скопируйте также файл toolbar1.bmp. Наконец, скопируйте из папки проекта ToolTip (см. листинг 8.4) файлы KWndEx.h и KWndEx.cpp.

Добавьте скопированные файлы в состав проекта. К настройкам проекта на вкладке Link нужно добавить библиотеку comctl32.lib.

Теперь откройте вкладку ResourceView в окне Workspace. В списке ресурсов от кройте папку String table и вызовите редактор таблицы строк двойным щелчком мыши на элементе String table.

Добавьте в таблицу строк следующие строки:

Идентификатор Строка

0Операции с файлами

1Параметры просмотра

2Параметры фигуры

300Тип фигуры

301Цвет фигуры

IDM_OPEN

Открыть файл

IDM_CLOSE

Закрыть файл

IDM_SAVE

Сохранить файл

IDM_EXIT

Выход из программы

IDM_SHOW_SHAPE

Показать рисунок

IDM_HIDE_SHAPE

Скрыть рисунок

IDM_RESIZE

Изменение размеров фигуры

IDM_ABOUT

Информация о программе

 

 

418

Глава 8. Элементы управления общего пользования

 

 

Обратите внимание на целочисленные идентификаторы 0, 1, 2, 300, 301, назна ченные в соответствии с описанной выше процедурой однозначной идентифика ции пунктов подменю.

Отредактируйте текст файла StatusBar.cpp так, чтобы он соответствовал лис тингу 8.5.

Листинг 8.5. Проект StatusBar

//////////////////////////////////////////////////////////////////////

// StatusBar.cpp #include <windows.h> #include <commctrl.h> #include <stdio.h> #include "KWndEx.h" #include "resource.h"

#define W 200 // ширина фигуры #define H 140 // высота фигуры enum ShapeSize { MAX, MIN };

typedef struct {

 

 

 

 

int id_shape;

// идентификатор фигуры

BOOL fRed;

// компонент красного

цвета

BOOL fGreen;

//

компонент

зеленого

цвета

BOOL fBlue;

//

компонент

синего цвета

int id_bright; // идентификатор яркости цвета } ShapeData;

#define ID_TOOLBAR 201 #define ID_STATUSBAR 202 #define NUM_BUTTONS 11 #define SEPARATOR_WIDTH 10

#define N_PARTS 4

HWND hwndToolBar;

HWND hwndStatusBar;

HWND InitToolBar(HWND hWnd);

void UpdateToolBar(ShapeData& sd);

void UpdateStatusBar(HWND hwnd, ShapeData& sd);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

{

MSG msg;

KWndEx mainWnd("StatusBar", hInstance, nCmdShow, WndProc, MAKEINTRESOURCE(IDR_MENU1), 100, 100, 400, 300);

/* Здесь такой же текст, как в листинге 8.1 */

}

//==================================================================== HWND InitToolBar(HWND hWnd) {

/* Здесь такой же текст, как в листинге 8.1 */

}

//====================================================================

Элементы управления главного окна

419

 

 

 

void UpdateToolBar(ShapeData& sd) {

/* Здесь такой же текст, как в листинге 8.1 */

}

//==================================================================== void UpdateStatusBar(HWND hwnd, ShapeData& sd) {

int paneWidth;

int aWidths[N_PARTS]; char text[100];

RECT rect;

int brightness;

int intensity[3] = { 85, 170, 255 }; GetClientRect(hwnd, &rect);

paneWidth = rect.right / N_PARTS; aWidths [0] = paneWidth;

aWidths [1] = paneWidth * 2; aWidths [2] = paneWidth * 3; aWidths [3] = -1;

SendMessage(hwndStatusBar, SB_SETPARTS, N_PARTS, (LPARAM)aWidths);

LoadString(GetModuleHandle(NULL), sd.id_shape, text, 100);

SendMessage(hwndStatusBar, SB_SETTEXT, 0, (LPARAM)text);

brightness = intensity[sd.id_bright - ID_DARK];

int red = sd.fRed? brightness : 0; sprintf(text, "Red = %d", red);

SendMessage(hwndStatusBar, SB_SETTEXT, 1, (LPARAM)text);

int green = sd.fGreen? brightness : 0; sprintf(text, "Green = %d", green);

SendMessage(hwndStatusBar, SB_SETTEXT, 2, (LPARAM)text);

int blue = sd.fBlue? brightness : 0; sprintf(text, "Blue = %d", blue);

SendMessage(hwndStatusBar, SB_SETTEXT, 3, (LPARAM)text);

}

//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

/* Определения локальных переменных из листинга 8.1 */

RECT rcSB; // позиция и размеры окна hwndStatusBar int sbHeight; // высота окна hwndStatusBar

HMENU hClickMenu; int itemID;

static UINT submenuID; char text[200];

switch (uMsg)

{

case WM_CREATE:

/* Здесь такой же текст, как в листинге 8.1 */

// Создание строки состояния

продолжение

 

420

Глава 8. Элементы управления общего пользования

 

 

Листинг 8.5 (продолжение)

hwndStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", hWnd, ID_STATUSBAR);

break;

case WM_SIZE:

SendMessage(hwndToolBar, TB_AUTOSIZE, 0, 0);

UpdateStatusBar(hWnd, shapeData);

SendMessage (hwndStatusBar, WM_SIZE, wParam, lParam); break;

case WM_NOTIFY:

/* Здесь такой же текст, как в листинге 8.1 */ break;

case WM_MENUSELECT:

itemID = LOWORD(wParam); // меню нулевого уровня hClickMenu = (HMENU)lParam;

if (HIWORD(wParam) & MF_POPUP) { if (hClickMenu == hMenu)

submenuID = itemID; else

itemID += 100 * (submenuID + 1);

}

else if (!itemID) itemID = -1;

SendMessage(hwndStatusBar, SB_SIMPLE, TRUE, 0); LoadString(GetModuleHandle(NULL), itemID, text, 200); SendMessage(hwndStatusBar, SB_SETTEXT, 255, (LPARAM)text); break;

case WM_COMMAND:

switch (LOWORD(wParam))

{

/* Обработка для кодов сообщений IDM_OPEN, IDM_CLOSE, … , IDM_RESIZE – как в листинге 8.1 */

case IDM_ABOUT: MessageBox(hWnd,

"StatusBar\nVersion 1.0\nCopyright: " "Finesoft Corporation, 2005.",

"About StatusBar", MB_OK); break;

default:

break;

}

UpdateToolBar(shapeData);

SendMessage(hwndStatusBar, SB_SIMPLE, FALSE, 0);

UpdateStatusBar(hWnd, shapeData);

InvalidateRect(hWnd, NULL, TRUE);