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

лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows

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

Модальный диалог

321

 

 

 

Второй параметр функции задает новый статус элемента управления. Если он имеет значение TRUE, то элемент становится разрешенным. Если параметру пере дается значение FALSE, то элемент становится запрещенным.

Также вы можете изменить следующие атрибуты элемента управления:

текст, связанный с элементом управления;

используемый шрифт;

цветовые атрибуты (только для элементов Static Text и Edit Box).

Текст (атрибут Caption)

Текст, связанный с элементом управления, можно изменить при помощи функ ции SetWindowText. Первому параметру функции передается дескриптор элемента управления, второму параметру — указатель на символьную строку или строко вая константа.

Напомним, что дескриптор элемента управления hWndControl в диалоговом окне hDlg можно получить по его идентификатору ID, вызвав функцию GetDlgItem:

hWndControl = GetDlgItem(hDlg, ID);

Используемый шрифт

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

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

Для изменения шрифта элемента управления необходимо послать сообщение WM_SETFONT окну этого элемента. Как известно, приложение может посылать со общения любому окну при помощи функции SendMessage. Интерпретация пара метров функции SendMessage зависит от кода сообщения. В данном случае функ ция вызывается со следующими параметрами:

SendMessage (

 

(HWND) hWnd,

// дескриптор окна-получателя сообщения

WM_SETFONT,

// код сообщения

(WPARAM) wParam;

// дескриптор назначаемого шрифта

(LPARAM) lParam;

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

);

 

Если параметр wParam равен NULL, то элемент управления будет использовать системный шрифт по умолчанию. Значение TRUE для параметра lParam заставляет элемент управления немедленно перерисовать себя.

Цветовые атрибуты

Элементы управления Static Text и Edit Box, используемые в режиме «только для чтения» (read only), посылают своему родительскому окну сообщение WM_CTLCOLORSTATIC перед тем, как они должны быть перерисованы.

В этом сообщении параметр wParam содержит дескриптор контекста устрой ства элемента управления, а параметр lParam — дескриптор самого элемента уп равления. Для использования этих дескрипторов в программе значение wParam следует преобразовать к типу HDC, а значение lParam — к типу HWND.

322

Глава 7. Диалоговые окна

 

 

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

Если приложение обрабатывает сообщение WM_CTLCOLORSTATIC, то возвращае мым значением должен быть дескриптор кисти типа HBRUSH, который система ис пользует для заполнения фона окна элемента управления.

Пример модификации атрибутов элемента управления

Применим новые знания на практике. Доработаем приложение DlgDemo1, чтобы улучшить дизайн модального диалогового окна. Для этого в листинге 7.1 нужно заменить определение функции AboutDlgProc следующим кодом:

//==================================================================== // Диалоговая процедура

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

static HWND hStatic1;

static LOGFONT lf; // обязательно static для инициализации полей нулями! HFONT hFont1;

switch (uMsg) { case WM_INITDIALOG:

hStatic1 = GetDlgItem(hDlg, IDC_STATIC_1);

//Создание логического шрифта Verdana lf.lfHeight = 28;

lstrcpy( (LPSTR)&lf.lfFaceName, "Verdana" ); hFont1 = CreateFontIndirect(&lf);

//Модификация шрифта для элемента hStatic1 SendMessage(hStatic1, WM_SETFONT, (WPARAM)hFont1, TRUE );

return TRUE;

case WM_CTLCOLORSTATIC:

// Модификация цветовых атрибутов элемента hStatic1 if ((HWND)lParam == hStatic1) {

SetTextColor((HDC)wParam, RGB(160, 0, 0)); SetBkMode((HDC)wParam, TRANSPARENT);

return (DWORD)GetSysColorBrush(COLOR_3DFACE);

}

break;

case WM_COMMAND:

switch (LOWORD(wParam)) { case IDOK:

case IDCANCEL: DeleteObject(hFont1); EndDialog(hDlg, 0); return TRUE;

}

break;

}

return FALSE;

}

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

Использование других элементов управления

323

 

 

 

Модификация шрифта для элемента управления IDC_STATIC_1 осуществляется в блоке обработки сообщения WM_INITDIALOG, а модификация цветовых атрибу тов — в блоке обработки сообщения WM_CTLCOLORSTATIC.

Если при обработке сообщения WM_CTLCOLORSTATIC выясняется, что оно посту пило от элемента управления с дескриптором hStatic1, то программа устанавлива ет темно красный цвет для вывода текста и возвращает дескриптор системной кисти, имеющей системный цвет COLOR_3DFACE. Возвращаемое значение позволя ет сохранить обычный фон элемента управления Static Text, сливающийся с фо ном диалогового окна. Если бы было необходимо сделать иным фон окна эле мента управления, то нужно было бы создать кисть (HBRUSH) желаемого цвета и использовать для возврата ее дескриптор.

Модифицированное приложение DlgDemo1 будет выводить диалоговое окно About DlgDemo1, показанное на рис. 7.15.

Рис. 7.15. Модальное диалоговое окно приложения DlgDemo1

Использование других элементов управления

Приложение DlgDemo1 демонстрирует использование в диалоговом окне элемен тов управления Picture и Static Text. Эти элементы реализуются в системе как до черние окна предопределенного оконного класса STATIC.

Следующая группа элементов управления, которая будет рассматриваться в этой главе, включает в себя кнопку (Button), флажок (Check box), переключатель (Radio button) и групповую рамку (Group box). Все эти элементы реализуются как дочерние окна предопределенного оконного класса BUTTON.

Кнопки

Кнопка (Button), называемая иногда «нажимаемой кнопкой» (push button), пред ставляет собой прямоугольник, внутри которого обычно располагается некото рый текст. Щелчок мышью на кнопке заставляет ее перерисовать себя, используя стиль 3D с тенью, чтобы выглядеть «нажатой». Отпускание кнопки мыши восста навливает начальный облик нажимаемой кнопки, а родительскому окну посыла ется сообщение WM_COMMAND с кодом уведомления BN_CLICKED.

Windows заботится о надлежащем поведении и внешнем облике кнопок. Впро чем, то же самое можно сказать о флажках и переключателях, которые тоже явля ются особыми разновидностями кнопок. Если кнопка имеет фокус ввода, то текст

324

Глава 7. Диалоговые окна

 

 

обводится штриховой линией, а нажатие и отпускание клавиши пробела имеет тот же эффект, что и щелчок мышью. Так утверждается в справочных материалах MSDN. К сожалению, выделение текста штриховой линией для кнопки с фоку сом ввода четко срабатывает только при использовании клавиатурного интерфей са, когда перевод фокуса ввода осуществляется при помощи клавиши Tab. Когда же приложение отслеживает положение курсора мыши самостоятельно и вызы вает функцию SetFocus для перевода фокуса ввода на соответствующий элемент, то никакого выделения текста почему то не происходит. В приложении DlgDemo2, рассматриваемом ниже, показан вариант решения этой проблемы с помощью вы зова функции DrawFocusRect.

Кнопки Button используются в основном для немедленного выполнения дей ствия, без сохранения какой либо индикации положения кнопки «включено/вык лючено».

Вприложении DlgDemo1 уже использовалась кнопка OK, которая находилась

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

Элемент управления «кнопка» размещается на форме диалога так же, как и другие элементы управления, — с помощью мыши, с предварительным выделе нием элемента Button на панели инструментов Controls. Затем надо вызвать окно свойств Push Button Properties и на вкладке General (см. рис. 7.13) в текстовом поле ID задать идентификатор кнопки, а в поле Caption указать текст, который будет отображаться на кнопке.

СОВЕТ

Редактор диалоговых окон предлагает в качестве идентификаторов для кнопок Button имена IDC_BUTTON1, IDC_BUTTON2 и им подобные. Рекомендуется заменять их именами, отражающими функциональное назначение кнопок. Этот совет относится и к определению идентификаторов других элементов управления.

На вкладке Styles (рис. 7.16) можно задать дополнительные свойства кнопки.

Рис. 7.16. Вкладка Styles окна свойств Push Button Properties

Флажок Default button назначает кнопке атрибут «применяемая по умолчанию».

Флажок Owner draw применяется для создания кнопок, за прорисовку которых отвечает окно владелец. Иногда кнопки этого стиля называют кнопками, опре деляемыми программистом.

Флажки Icon или Bitmap позволяют указать, что вместо текста будет отобра жаться пиктограмма или растровый образ.

Использование других элементов управления

325

 

 

 

Флажок Multiline используется, если текст слишком длинный, чтобы уместиться на кнопке в одну строку.

Флажок Flat создает плоскую кнопку.

Опции Horizontal alignment и Vertical alignment позволяют выбрать вариант вы равнивания текста внутри ограничивающего прямоугольника.

Чаще всего значения свойств на вкладке Styles оставляют по умолчанию, то есть в том состоянии, которое показано на рис. 7.16.

Наиболее сложны в реализации кнопки, определяемые программистом (Owner draw), поэтому этот вопрос стоит рассмотреть отдельно.

Кнопка Owner draw

Если при задании свойств кнопки был установлен флажок Owner draw, то в опреде лении кнопки в файле описания ресурсов появится стиль BS_OWNERDRAW. Кнопка стиля BS_OWNERDRAW отличается от обычной кнопки тем, что при необходимости перерисовки она посылает своему родительскому окну сообщение WM_DRAWITEM. Это происходит при первоначальном создании кнопки, при ее нажатии или от пускании, при получении или потере фокуса ввода и во всех других случаях, ког да требуется перерисовка элемента управления.

Когда приложение получает сообщение WM_DRAWITEM, параметр wParam этого сообщения содержит идентификатор элемента управления, а параметр lParam яв ляется указателем на структуру типа DRAWITEMSTRUCT. Сообщение WM_DRAWITEM может поступать не только от кнопок со стилем Owner draw, но и от элементов уп равления Combo box, List box и List view, если они имеют аналогичный стиль.

Структура DRAWITEMSTRUCT определена следующим образом:

typedef struct {

UINT

CtlType;

UINT

CtlID;

UINT

itemID;

UINT

itemAction;

UINT

itemState;

HWND

hwndItem;

HDC

hDC;

RECT

rcItem;

ULONG_PTR itemData; } DRAWITEMSTRUCT;

Поле CtlType позволяет указывать тип элемента управления. Возможные зна чения этого поля приведены в табл. 7.2.

Таблица 7.2. Возможные значения поля CtlType

Значение поля CtlType

Тип элемента управления

 

 

ODT_BUTTON

Кнопка со стилем Owner draw

ODT_COMBOBOX

Combo box со стилем Owner draw

ODT_LISTBOX

List box со стилем Owner draw

ODT_LISTVIEW

List control со стилем Owner draw

ODT_MENU

Пункт меню со стилем Owner draw

ODT_STATIC

Static control со стилем Owner draw

ODT_TAB

Tab control со стилем Owner draw

 

 

326

Глава 7. Диалоговые окна

 

 

Вполе CtlID передается идентификатор элемента управления. Оно не исполь зуется, если источником сообщения является пункт меню.

Вполе itemID передается идентификатор пункта меню или индекс строки

вэлементе управления Combo box или List box.

Поле itemAction определяет тип действия, связанного с элементом управления. Его значением может быть одна из констант, указанных в табл. 7.3.

Таблица 7.3. Возможные значения поля itemAction

Значение itemAction

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

 

 

ODA_DRAWENTIRE

Элемент управления требует перерисовки

ODA_FOCUS

Элемент управления получил или потерял фокус ввода. Текущее

 

состояние можно определить, проверяя флаг ODS_FOCUS

 

â ïîëå itemState

ODA_SELECT

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

 

(нажатого) элемента. Текущее состояние можно определить,

 

проверяя флаг ODS_SELECTED в поле itemState

 

 

Поле itemState определяет текущее визуальное состояние элемента управления в виде комбинации битовых флагов. Применение флагов ODS_FOCUS и ODS_SELECTED поясняется в табл. 7.3. Информацию о других флагах можно найти в справочных материалах MSDN.

Поле hDC содержит дескриптор контекста устройства для дочернего окна эле мента управления. Этот дескриптор нужно использовать во всех функциях рисо вания.

Поле rcItem содержит размеры прямоугольника, ограничивающего элемент управления. Прямоугольник определен в контексте устройства с дескриптором hDC.

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

Пример использования кнопки Owner draw

Технику применения кнопки со стилем Owner draw покажем на примере модифи кации приложения DlgDemo1. В новой версии программы, названной DlgDemo2, диалоговое окно About… должно стать еще более привлекательным за счет совме щения картинки и кнопки OK.

Создайте новый проект с именем DlgDemo2. Скопируйте из папки проекта DlgDemo1 (см. листинг 7.1) в папку проекта DlgDemo2 файлы с расширениями .cpp,

.h и .rc, скорректировав их имена заменой DlgDemo1 на DlgDemo2. Добавьте эти фай лы в состав проекта. В файле DlgDemo2.cpp все вхождения подстроки DlgDemo1 за мените подстрокой DlgDemo2.

Скопируйте также в папку проекта файл Butterfly.bmp, заменив его имя на BtnActive.bmp. Этот растровый образ будет использоваться для изображения ак тивной кнопки, то есть кнопки, имеющей фокус ввода. Создайте при помощи ка кого либо графического редактора еще два варианта растрового изображения. Файл BtnNoActive.bmp должен содержать изображение пассивной кнопки в серой гамме цветов, а файл BtnPress.bmp будет хранить изображение нажатой кнопки

Использование других элементов управления

327

 

 

 

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

Доработку программы начнем с модификации шаблона диалога IDD_ABOUT. Для этого надо перейти на вкладку Resource View в окне Workspace и дважды щелкнуть мышью на элементе IDD_ABOUT. В результате будет вызван редактор диалоговых окон с шаблоном диалога, внешний вид которого показан на рис. 7.12.

Откройте окно свойств диалогового окна IDD_ABOUT и замените текст About DlgDemo1строкой AboutDlgDemo2. Затем откройте окно свойств элемента IDC_STATIC_1 и замените текст DlgDemo1 строкой DlgDemo2.

Удалите кнопку OK и рисунок с бабочкой. Выделите группу надписей, как по казано на рис. 7.12, и, используя клавиши стрелки, сместите эту группу немного правее. Увеличьте высоту элемента DlgDemo2, а остальные две надписи передвиньте к нижней границе окна.

На месте удаленного рисунка поместите элемент управления Button и задайте его размеры, как показано на рис. 7.17. Теперь вызовите окно свойств кнопки и укажите идентификатор IDOK, а в поле Caption удалите весь текст. Затем перей дите на вкладку Styles и установите флажок Owner draw.

Ниже кнопки IDOK поместите еще одну кнопку стандартного размера. В окне свойств кнопки задайте идентификатор ID_ABOUT_HELP, а в поле Caption введите строку Help. После всех указанных манипуляций шаблон диалога примет вид, по казанный на рис. 7.17.

Рис. 7.17. Модифицированный шаблон диалога

Теперь обеспечим необходимые растровые ресурсы для программы. Перейди те опять на вкладку Resource View в окне Workspace. Откройте папку Bitmap и уда лите элемент IDB_BUTTERFLY. Добавьте к проекту три растровых ресурса в режиме Import, назначив им следующие идентификаторы:

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

Ôàéë

 

 

IDB_PICT_0

BtnNoActive.bmp

IDB_PICT_1

BtnActive.bmp

IDB_PICT_2

BtnPress.bmp

 

 

1Вы можете взять готовые файлы с этими изображениями, если скачаете файлы к данной книге с сайта издательства http://www.piter.com.

328

Глава 7. Диалоговые окна

 

 

Осталось внести необходимые изменения в код программы. Нет нужды при водить полный текст файла DlgDemo2.cpp, поскольку изменения касаются только функции AboutDlgProc. В листинге 7.2 содержится ее новая редакция.

Листинг 7.2. Диалоговая процедура AboutDlgProc в файле DlgDemo2.cpp

//==================================================================== BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,

LPARAM lParam)

{

HINSTANCE hInst; static HWND hStatic1;

static LOGFONT lf; // обязательно static для инициализации полей нулями! static HFONT hFont1, hFont2;

static HBITMAP hPict0;

// изображение пассивной кнопки

static HBITMAP hPict1;

// изображение активной кнопки

static HBITMAP hPict2;

// изображение нажатой кнопки

static HBRUSH hBrush0;

// фон пассивной кнопки

static HBRUSH hBrush1;

// фон активной кнопки

static HBRUSH hBrush2;

// фон нажатой кнопки

static HBRUSH hCurBrush; // текущий фон

static HDC hMemDC;

// контекст устройства в памяти

static BITMAP bm;

// параметры растра

LPDRAWITEMSTRUCT pdis;

static HWND hBtnOk, hBtnHelp; static RECT rcBtnHelp, rect; int x0, y0 = 0;

switch (uMsg) { case WM_INITDIALOG:

hInst = GetModuleHandle(NULL);

hStatic1 = GetDlgItem(hDlg, IDC_STATIC_1); hBtnOk = GetDlgItem(hDlg, IDOK);

hBtnHelp = GetDlgItem(hDlg, ID_ABOUT_HELP); GetWindowRect(hBtnHelp, &rcBtnHelp);

//Создание логического шрифта Verdana lf.lfHeight = 28;

lstrcpy( (LPSTR)&lf.lfFaceName, "Verdana" ); hFont1 = CreateFontIndirect(&lf); lf.lfHeight = 20;

hFont2 = CreateFontIndirect(&lf);

//Модификация шрифта для элемента hStatic1 SendMessage(hStatic1, WM_SETFONT, (WPARAM)hFont1, TRUE );

//Инициализация растровых изображений кнопки IDOK

hPict0 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PICT_0));

hPict1 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PICT_1));

hPict2 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PICT_2));

hBrush0 = CreateSolidBrush(RGB(214, 214, 214));

hBrush1 = CreateSolidBrush(RGB(149, 242, 242));

hBrush2 = CreateSolidBrush(RGB(83, 52, 254));

GetObject(hPict0, sizeof(bm), (LPSTR)&bm);

SetFocus(hBtnHelp);

Использование других элементов управления

329

 

 

 

return FALSE; // чтобы фокус ввода остался на hBtnHelp

case WM_CTLCOLORSTATIC:

// Модификация цветовых атрибутов элемента hStatic1 if ((HWND)lParam == hStatic1) {

SetTextColor((HDC)wParam, RGB(160, 0, 0)); SetBkMode((HDC)wParam, TRANSPARENT);

return (DWORD)GetSysColorBrush(COLOR_3DFACE);

}

break;

case WM_DRAWITEM: // сообщение, используемое для // перерисовки кнопки Owner draw

pdis = (LPDRAWITEMSTRUCT)lParam;

hMemDC = CreateCompatibleDC(pdis->hDC); // Проверяем, какие события произошли switch (pdis->itemAction) {

case ODA_FOCUS:

if (pdis->itemState & ODS_FOCUS) { SelectObject(hMemDC, hPict1); hCurBrush = hBrush1;

}

else {

SelectObject(hMemDC, hPict0); hCurBrush = hBrush0;

}

break;

case ODA_SELECT:

if (pdis->itemState & ODS_SELECTED) { SelectObject(hMemDC, hPict2); hCurBrush = hBrush2;

}

break;

default:

SelectObject(hMemDC, hPict0); hCurBrush = hBrush0;

}// end of switch

//Вывод изображения кнопки с учетом произошедших событий FillRect(pdis->hDC, &pdis->rcItem, hCurBrush);

x0 = ((pdis->rcItem.right - pdis->rcItem.left) - bm.bmWidth) / 2;

//Картинка ...

BitBlt(pdis->hDC, x0, y0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);

//а теперь текст SelectObject(pdis->hDC, hFont2); SetBkMode(pdis->hDC, TRANSPARENT);

SetTextColor(pdis->hDC, RGB(255, 255, 255)); TextOut(pdis->hDC, 32, 74, "OK", 2); SetTextColor(pdis->hDC, RGB(100, 100, 100)); TextOut(pdis->hDC, 30, 72, "OK", 2);

//рамка вокруг кнопки

FrameRect(pdis->hDC, &pdis->rcItem, (HBRUSH)GetStockObject(BLACK_BRUSH));

DeleteDC(hMemDC); // !!! не забудьте удалить hMemDC

продолжение

330 Глава 7. Диалоговые окна

Листинг 7.2 (продолжение) return TRUE;

case WM_SETCURSOR: // отслеживание фокуса ввода для курсора мыши if ((HWND)wParam == hBtnOk)

if (GetFocus() != hBtnOk) SetFocus(hBtnOk);

if ((HWND)wParam == hBtnHelp) { if (GetFocus() != hBtnHelp) {

SetFocus(hBtnHelp);

// Рисуем пунктирную рамку вызовом DrawFocusRect SetRect(&rect, 3, 3, rcBtnHelp.right - rcBtnHelp.left - 3,

rcBtnHelp.bottom - rcBtnHelp.top - 3); DrawFocusRect(GetDC(hBtnHelp), &rect);

}

}

return FALSE;

case WM_COMMAND:

switch (LOWORD(wParam)) { case ID_ABOUT_HELP:

MessageBox(hDlg, "В этом диалоговом окне работает кнопка 'Owner draw'", "HELP", MB_OK);

return TRUE;

case IDOK: case IDCANCEL:

DeleteObject(hFont1);

DeleteObject(hFont2); EndDialog(hDlg, 0); return TRUE;

}

break;

}

return FALSE;

}

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

Обратите внимание на обработку сообщений WM_DRAWITEM и WM_SETCURSOR. При обработке сообщения WM_DRAWITEM на поверхности кнопки IDOK отобра

жается растровое изображение, соответствующее текущему состоянию кнопки. Для этого в контекст устройства в памяти hMemDC выбирается один из дескрипто ров растров, hPict0, hPict1 или hPict2. Отображение осуществляется позже вызо вом функции BitBlt. Но сначала поверхность кнопки заполняется цветом фона при помощи функции FillRect с использованием текущей кисти hCurBrush. Цвет кисти должен совпадать с цветом фона используемой картинки. В этом случае картинка плавно вписывается в поверхность кнопки, и пользователь не заметит, что рису нок меньше, чем кнопка. После отображения растра выводится текст OK с приме нением более красивого шрифта, чем системный шрифт.

В приложении приходится решать еще одну проблему. Дело в том, что кнопка IDOK должна изменять свой облик в зависимости от того, имеет она фокус ввода или нет. Если пользователь общается с приложением через клавиатурный интер фейс, то никаких проблем с управлением фокусом ввода не будет. При нажатии клавиши Tab фокус автоматически переходит от одного элемента управления со стилем WS_TABSTOP к другому элементу, имеющему такой же стиль.