Азбука программирования в Win32 API

         

ListBox Example



_%«! • families1

Berdiev Ivanov Jackson

Johnson

Mamedov Novozhenov

[Message-about КЬаЪ1Ь|Ш1Ш№Ш;Ь:-

Рис. IO. Диалоговое окно, содержащее окно списка

Следующим шагом в изучении органов управления, применяющихся в диалоговых окнах, является изучение достаточно интересного элемента, который называется

ОКНО РЕДАКТИРОВАНИЯ

Окно редактирования - это один из наиболее сложных (с точки зрения реализации, а не использования) и наиболее интересных элементов управления. Фактически этот элемент представляет собой небольшой текстовый редактор, который позволяет вводить текст, редактировать его, копировать в буфер, вставлять из буфера и т. д. Окна редактирования могу быть однострочными и многострочными. Однострочные окна редактирования обычно используются для ввода небольших элементов текста. Например, в Program Manager'e для запуска программы пользова­телю необходимо ввести имя и командную строку этой программы. Примером редактора, большую часть функциональности которого обес­печивается за счет многострочного окна редактирования, является Notepad, который поставляется со всеми версиями Windows.

Окно редактирования можно создать как в файле ресурсов, так и как отдельное дочернее окно, указав при этом предопределенный класс «edit». В этом разделе мы разберем оба случая использования окна редак­тирования .

Перед тем как начать изучение, давайте вспомним, что все поведение элемента управления зависит от того, какие стили мы укажем при его создании. Все стили окна упомянуты в winuser.h. Все они начинаются с букв ES_. Эти стили приведены в табл. 31.

Таблица 31. Стили окна редактирования



Стиль

Значение

 

Описание

 

esj.eft

 

OxOOOOL

 

Текст в окне редактирования выравнивается по

 

 

 

 

 

левому краю

 

F.SJTENTFR

 

0x000 IL

 

Текст в окис редактирования выравнивается по

 

 

 

 

 

правому краю

 

HS RIGHT

 

0x00021.

 

Текст в окне редактирования выравнивается по центру

 

ES MULTILINE

 

Ox()004L

 

Создается многострочнос окно редактирования

 

es uppercase

 

0x00081.

 

Вводимый текст преобразуется в прописные буквы

 

F.S  LOWERCASF.

 

0x00 101.

 

Вводимы:'] текст преобразуется в строчные буквы

 

FS  PASSWORD

 

0x0020!.

 

Все вводимые символы отображаются в виде звез-

 

 

 

 

 

дочек

 

<


130

Окончание табл. 31

(л и .'1 ь

 

Значение

 

Описание

 

ES_AUTOVSCROLL

 

Ox0040L

 

При необходимости текст в многострочном окне

 

ES_AUTOHSCROLL

 

OxOOSOL

 

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

 

ES_NOHIDFSF.L

 

0x0 100 1.

 

скроллируется по горизонтали При потере окном редактирования фокуса ввода

 

 

 

 

 

выделение с текста не снимается

 

ES_OEMCONVERT

 

0x04001.

 

Вводимые символы из одного набора преобразуются

 

ES_READONEY

 

OxOSOOL

 

в символы из друг ого наоора Текст в окне редактирования можно только просмат-

 

ESJWANTRETURN

 

Ox 1 OOOL

 

ривать, но не редактировать При нажатии клавиши Enter в многострочном окне

 

ESJNUMBER

 

0x20001,

 

система вставляет в текст символ возврата каретки Разрешается осуществлять ввод только цифр

 

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

#incl''dc <windo\vs.h>

#definc ID Edit 101

HINSTANCE Must;

ERESUET CALLBACK EditDemoWndProc ( HWND, UINT, U1NT, EONG );

int WINAPI WinMain (HINSTANCE hlnstance, HINSTANCE hPrevInstance,

LPSTR IpszCmdParam, int nCmdShow )

i i

HWND hWnd ;

WNDCLASS WndClass ;

MSG Msi-

char szClassName[] - "EditDemo";

hlnst — hlnstance; /* Registering our window class */ /* Fill WNDCLASS structure */

WndClass.slyle - CSJIREDRAW | CS VREDRAW;

WndClass.lpfnWndProc = EditDcmoWndProc;

WndClass.cbClsExtra - 0;

WndClass.cbWndExtru ---- 0;

WndClass.hlnstance = hlnstance ;

WndClass.hlcon ----- Loacllcon (NULEJDI APPLICATION),



WndClass.hCursor = LoadCursor (NULL, IDC_ARROW); WndClass.librBackground - (HBRUSH) GctStockObject (WHITE_BRliSH); WndClass.lpszMcnuNamc - NULL; WndClass.lpszClassNamc ~ sxClassName;

if ( !RegisterClass(&WndClass) )

f

MessageBox(NULL,"Cannot register class", "Error", MB_OK); return 0;

} liWud = C'reateWmdow(szClassName, "EditDemo",

WS_OVERLAPPEDWINDOW, CWJJSEDEFAULT.

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, NULL, NULL,

hlnstance.NULL); if(!hWnc!)

t MessageBo,x(NULL,"Cannot create window ', "Error", MBJDK);

return 0;

/* Show our window */

ShowWindow(hWnd,nCmdSliow);

UpdateWindow(hWnd); /* Beginning of messages cycle */

while(GetMessage(&Msg, NULL, 0, 0))

!

TranslateMessage(&Msg);

DispatchMessagef&Msg); } return Msg.wParam;

LRESULT CALLBACK EditDemoWndProc (HWND hWnd, UINT Message,

UFNT wParam, LONG IParam )

'static HWND hEditWnd; RECT Rcct;

switch(Message)

!

case WMJTREATE:

GetClientRect(hWnd, &Rect);

hEditWnd = CreatcWindow("edit", NULL,

WS_CHILD | WSJVISIBLE | WS HSCROLL! WS VSCROLL| WS BORDER | ES_LEFT | ES MULTILLNE [ ES_AUTOHSCROLL | ES^AUTOVSCROLL, 0, 0, 0, 0,

132

hWnd,

(HMENU) ID_Edit, hlnst, NULL); return 0; case WM_SIZE: MoveWindow(liEditWnd, 0, 0, LOWORD(lParam), HIWORD(IParam),

TRUE); return 0;

case WM_SETFOCUS: SetFocus(hEditWnd); return 0;

case WM_DESTROY: PostQuitMessage(O); return 0; } return DefWindowProc(hWnd,Message,wParam, IParam);

Вид окна, создаваемого программой, показан на рис. 11.

Эта программа создает окно редактирования, которое располагается по­верх основного окна программы. Фактически в программе создается тексто­вый редактор, позволяющий осуществлять набор и редактирование текста, выделять части текста. Выделенные части текста могут быть перемещены в Clipboard посредством нажатия клавиш Shift-Delete, а после нажатия клавиш Shift-Insert текст из Clipboard'a может быть вставлен в окно.

В EditDemo

Это окно, представляющее собой простейший текстовый редактор, создано на основе многострочного окна редактирс ния.



.Этот текст выдепен и подготовлен для перемещения

ib 6i

Рис. 11. Окно редактирования с невыделенным и выделенным текстом

Управление окном редактирования, как и всех остальных элемен­ тов управления, осуществляется посредством посылки окну сооб­щений.

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

Следующие пять сообщений не имеют параметров, а их действия очевидны из их названия:

WM_COPY - выделенная часть текста копируется в Clipboard;

WM_PASTE - содержимое Clipboard'a копируется в окно редактиро­вания (данные вставляются только в том случае, если в Clipboard'e нахо­дится текст);

WM_CUT - выделенная часть текста удаляется из окна редактирова­ния и помещается в Clipboard;

WM_CLEAR - выделенная часть текста удаляется из окна редактиро­вания и не помещается в Clipboard;

WM_UNDO - отменяется последняя операция.

Для того чтобы получить границы выделения текста, необходимо ис­пользовать сообщение EM_GETSEL. Младшее слово возвращаемого значения содержит начальную, а старшее слово - конечную позицию выделения плюс 1. Другими словами,

DWORD dwPosition = SendMessage(hEditWnd, EMJ3ETSEL,

(WPARAM) 0, (LPARAM) 0); WORD wBcginPosition = LOWORD(dwPosition); WORD wEndPosition = HIWORD(dwPosition) - 1;

Если программе необходимо выделить часть текста, то она может ис­пользовать сообщение EM_SETCEL, в IParam которого необходимо указать начальную и конечную позиции выделения:

SendMessagefhEditWnd, EM^SETSEL, 0, MAKELONG(wBeginPosition, wEndPosition));

С помощью сообщения EM_REPLACESEL программа может заме­нить выделенный текст на другой:

SendMcssage(hEditWnd, EM_REPLACESEL, О, (LPARAM) pszNcwTcxt);

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



SendMessagefhEditWnd, EM_GETLINE, (WPARAM) nLine, (LPARAM) (LPCSTR) pBufler);

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

На этом завершается рассмотрение стандартных элементов управления.

Должен заметить, что в Win32 включены новые, так называемые об­щие элементы управления (common controls). Многие из них уже были реализованы в приложениях, работающих в среде Windows 3.x, но до их документирования в Windows 3.x дело не дошло. Встречались попытки описать их реализацию в частности, в MSDN были описаны строка состояния, панель инструментов (toolbar), окно просмотра деревьев. Поэтому, наверное, можно сказать, что появление общих элементов управления ожидалось. И теперь мы приступаем к их изучению.

ОБЩИЕ ЭЛЕМЕНТЫ УПРАВЛЕНИЯ

Перед тем, как мы начнем изучение работы непосредственно элемен­тов управления, мы должны научиться подключать библиотеку, реали­зующую эти элементы. Для нормальной работы программы с общими элементами управления необходимо выполнить два шага. Первым шагом является подключение к программе файла заголовков commctrl.h. Вторым шагом является подключение при линковашш непосредственно библио­теки. Она называется comctl32.dll.

Перед использованием любого из общих элементов управления необ­ходимо загрузить эту библиотеку. Это делается с помощью функции InitCommonControls(), которая описана в файле commctrl.h как

void InitCommonContorls(void);

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

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



Первым элементом управления, работу с которым мы рассмотрим, бу­дет строка состояния (status bar). Ранее мы использовали ее в наших демонстрационных программах, но работу с ней не изучали.

РАБОТА СО СТРОКОЙ СОСТОЯНИЯ



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

Строка состояния может быть включена в описание диалогового окна в файле ресурсов. Но так как окно состояния является стандартным окном, то, естественно, что для его создания могут быть использованы функции и стандартные функции CreateWindowQ и CreateWindowExQ. При этом в качестве имени класса окна необходимо задать макро «STATUSCLASSNAME». В зависимости от того, какую систему коди­ровки использует прснрамма, можно воспользоваться также и «истинным» именем класса (msctls_statusbar32). В commctrl.h эти макро и имена описаны следующим образом:

#ifdcf_WIN32

«define STATUSCLASSNAMEW

«define STATUSCLASSNAMEA

«ifdefUNICODE

«define STATUSCLASSNAME

«else

«define STATUSCLASSNAME

#cndif

#else

«define STATUSCLASSNAME

«endif

L"msctls_statusbar32" "msctls statusbar32"

STATUSCLASSNAMEW STATUSCLASSNAMEA

"msctls statusbar"

136

Тем не менее, для создания окна состояния предусмотрена и отдель­ная функция CreateStatusWindowQ. В файле commctrl.h эта функция определяется так:

WINCOMMCTRLAPI HWND WINAPI CreateStatusWindowA(LONG style,

LPCSTR IpszText, HWND hwndParent, UINT wID);

WINCOMMCTRLAPI HWND WINAPI CreatcStatusWindowW(LONG style,

LPCWSTR IpszText, HWND hwndParent, UINT wID);

#ifdcf UNICODE

«define CreateStatusWindow

«else

«define CreateStatusWindow

«cndif

CrcateStatusWindowW



CreateStatusWindowA

Как можно узнать из описания функции, она требует передачи ей четырех аргументов. Первый аргумент, style, должен определять стиль создаваемого окна. У строки состояния есть единственный собственный стиль, SBARS_SIZEGRIP, который позволяет в правый угол строки состояния добавить «ручку» (внешне, честно говоря, на ручку это совсем не похоже) для изменения ее размеров. Но наличие у строки состояния единственного стиля не мешает комбинировать его со стандартными стилями окон, например с WS^CHILD и WSJVISIBLE.

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

Третий аргумент - hwndParent - тоже не требует особых объяснений. Он является хэндлом родительского окна строки состояния.

Наконец, четвертый аргумент - uID - идентификатор окна со­стояния.

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

После того, как строка состояния прорисована, иногда бывает необхо­димо разделить ее на несколько панелей для того, чтобы в каждой панели отображать информацию, логически не связанную с отображаемой в других панелях. Для того чтобы сделать это, необходимо послать строке состояния сообщение SB_SETPARTS. При этом wParam этого сообщения должен определять число панелей, a IParam должен содержать указатель

на массив целых чисел (число элементов массива должно быть равно wParam). Каждый элемент в этом массиве должен определять позицию (в координатах родительского окна) правой границы соответствующей части. Если элемент равен -I, то границей панели считается правая гра­ница строки состояния. В случае успешного завершения операции функ­ция, с помощью которой послано сообщение, возвращает TRUE. Значение FALSE должно заставить программиста поискать ошибку в собственной программе.

Приложение может определить число панелей, на которые разде­лена строка состояния, и их координаты с помощью сообщения SB_GETPARTS. Если IParam этого сообщения равен нулю, то функ­ция, пославшая сообщение, возвращает число панелей строки состоя­ния. При этом значение wParam роли не играет. Для того чтобы по­лучить координаты панелей, wParam должен определять число панелей, для которых нужно получить координаты, a IParam должен указывать на массив целых чисел, в который будут записаны эти координаты. В этом случае функция также возвращает число панелей. Если при обработке сообщения произошла какая-то ошибка, то функ­ция возвращает нулевое значение.



Итак, строка состояния сформирована. Но зачем она нужна без ото­браженной информации? Для того чтобы отобразить определенный текст в строке состояния, нужно послать ей сообщение SB SETTEXT. В качестве wParam этого сообщения используется результат логического сложения двух величин. Первая (iPart) - номер (считая от нуля) панели, в которой необходимо отобразить текст. Вторая (иТуре) определяет, как будет выглядеть текст. В качестве иТуре могут быть использованы значения, приведенные в табл. 32.

Таблица 32. Возможные типы строки состояния

Тип

Значение

 

Описание

 

 

 

0x0000

 

Текст кажется вдавленным в панель

 

 

 

SBT_NOBORDERS

 

0x0 I 00

 

Панель прорисовывается без ограничительны

 

\

 

 

 

 

 

линии

 

 

 

SBT POPOUT SBTJITLREADING

 

0x0200 0x0400

 

Панель прорисовывается выпуклой Используется для языков, в которых чтение и

 

дет

 

SnT_OWNERDRAW

 

Ох 1 000

 

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

 

окно

 

IParam сообщения должен содержать указатель на строку, которую необходимо отобразить в панели строки состояния.

А теперь, для того, чтобы прочитать текст в панели, необходимо стро­ке состояния послать сообщение WM_GETTEXT. wParam этого сообще­ния должен содержать номер панели, a IParam - указатель на строку, в которую будет записан текст, содержащийся в панели.

Это основные сообщения, используемые при работе со строкой со­стояния.

Для иллюстрации некоторых возможностей строки состояния, о кото­рых я только что рассказал, рассмотрим несколько измененную програм­му из раздела о кнопках. Из-за мелких отличий не буду приводить текст программы целиком, а приведу только текст диалоговой функции:

BOOL CALLBACK ButtonsExampleDialogProc(HWND liDIg,

UiNT Message, WPARAM wParam, LPARAM IParam)



int i;

char cMyMessage[80];

RECT Rect;

int nBorders[3];

switch(Message)

case WMJNITDIALOG: // Set states of controls

SendDIgItemMessage(hDlg, nRadioButtonld, BM^SETCHECK,

BST_CHECKED, 0);

for(i - IDC_CHECKBOXI; i <= IDC_CHECKBOX3; i++) if(uCheckBoxesState[i - 208])

SendDlgItemMessage(hDlg, i, BM_SETCHECK, BST_CHECKED, 0); GetClientRect(hDIg, &Rect); nBorders[0] = Rect.right / 3; nBorders[l]-Reel.right/3 * 2; nBorders[2] = -1; SendDlgItemMessage(hDlg, IDC_STATUSBAR, SB_SETPARTS, 3,

(LPARAM) nBorders); return TRUE; case WM__COMMAND: switch(LOWORD(wParam))

case IDCJIADIOBUTTONI: case IDC_RADIOBUTTON2: case IDC_RADIOBUTTON3:

sprintf(cMyMessage,"RadioButton%d", LOWORD(wParam) - 203);

SendDlgItemMessage(hDlg,IDC_STATUSBAR,SB_SETTEXT, (WPARAM) 0, (LPARAM) cMyMessage);

CheckMenuRadioItem(GetSubMenu(GetSubMenu(GetMenu(hWnd), 1),  0), IDM_RadioButtonl,IDM_RadioButton3, LOWORD(wParam) - 102, MF_BYCOMMAND); return FALSE; caseIDC_CHECKBOXl: case IDC_CHECKBOX2: case IDC_CHECKBOX3:

sprintf(cMyMessage,"CheckBox%d", LOWORD(wParam) - 207); SendDlgItemMessage(hDlg,IDC_STATUSBAR,SB_SETTEXT,

(WPARAM) 1, (LPARAM) cMyMcssage); i = LOWORD(wParam) - 208; uCheckBoxesStatc[i] = uChcckBoxesStale[i] == MF_CHECKED ?

MFJJNCHECKED : MF_CHECKED;

CheckMenuttem(GetSubMenu(GetSubMenu(GetMenu(hWnd), I), 1), LOWORD(wParam) - 103, uCheckBoxesStatc[i]); return FALSE; caseIDC_BUTTONl:

SendDlgItemMessage(hDlg, IDC_STATUSBAR, SB_SETTEXT, (WPARAM) 2, (LPARAM) "PushButton"); return TRUE; case IDC_BUTTON2; // Save the state of RadioButtons

i = IDC_RADIOBUTTON 1; while(!SendDlgItemMessage(hDlg, i, BM_GETCHECK, 0, 0))

i++;

nRadioButtonld = i; // Save the state of CheckButtons

for(i = IDC_CHECKBOXl; i <= IDC_CHECKBOX3; i++) uCheckBoxesState[i - 208] = SendDlgItemMessage(hDlg, i, BM_GETCHECK, 0, 0) == 0 ? MFJJNCHECKED : MF_CHECKED; EndDialog(hDlg,0); return TRUE; } break;

} return FALSE;

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



Сравните внешний вид строки состояния, приведенной на рисунке, и строки состояния из раздела о кнопках.

Buttons Example



-^^fffpel-r

.. ...„.      ...

Q-"R"a'df6eutton2®,

'

IBadioButtpnS.

Рис. 12. Диалоговое окно со строкой состояния

РАБОТА СО СПИНОМ



Иногда в приложениях встречаются ситуации, в которых полосы про­крутки (скроллирования) не нужны, достаточно только кнопок «вверх» и «вниз». Ярким примером такой ситуации может служить окно, открывае­мое в WinWord'e для Windows'95 при необходимости определить парамет­ры страницы. Естественно, полоса прокрутки для того, чтобы чуть уве­личить значение размера бумаги или сделать поля поменьше, не нужна. Для подобных случаев в Win32 предусмотрен новый элемент управления, называемый спином. Спин является особым видом линейки прокрутки и состоит только из кнопок со стрелками, которые находятся на концах линейки, и не включает линейки прокрутки. Обычно спин используется в одном из двух вариантов. Во-первых, он может применяться а 1а маленькая линейка прокрутки. В этом случае его называют up-down control'oM. Во-вторых, часто он используется в сочетании с другим элементом управле­ния, называемым в этом случае buddy window (приятельским окном). Как правило, этим приятельским окном оказывается окно редактирования. Кстати, в приведенном ниже примере именно окна редактирования и оказываются приятельскими окнами. В этом случае элемент управления называется spin'oM. В данном разделе под словом «спин» будем понимать как up-down control, так и собственно спин.

Спин   может   создаваться   и  в  составе  диалогового   окна   в   файле ресурсов,  и  как  обычное  окно  посредством   использования   функций или _..„.. .....__.,_..,ч. При этом в качестве имени класса

. Соответствующее описание

шдом()

[ЧеобходймЬ указывать можно встретить в

сошшсгц-и:

#ifdef JWIN32

#defme UPDOWN_CLASSA

#define UPDOWN_CLASSW

#ifdefUNICODE

#dcfine UPDOWN_CLASS

#elsc

«define UPDOWN_CLASS

#endif

#else



#define UPDOWN_CLASS

#endif

"msctls_updown32" L"msctls_updown32"

UPDOWN_CLASSW UPDOWN_CLASSA

"msctls_updown"

Тем не менее, для создания спина создана специальная функция CreateUpDownControlQ, описание которого мы можем найти в файле commctrl.h:

WINCOMMCTRLAPI HWND WINAPI CreateUpDownControl(

DWORD dwSlylc, int x, int у, int ex, int cy, HWND hParent. int nID, HINSTANCE hlnsl, HWND hBuddy, int nUpper, int nLower, int nPos);

Таблица 33. Стили спина

Стиль

Значение

Описание

UDS  WRAP

UDS_SETBUDDYINT UUS_ALIGNRIGHT

UDS_ALIGNLEFT UDS_AUTOBUDDY

UDS_ARROWKEYS

UDDS_MORZ

UDS NOTHOUSANDS

0x0001

0x0002 0x0004

0x0008 0x0010

0x0020

0x0040 0x0080

При достижении максимальной позиции отсчет начинается вновь с минимальной позиции и наоборот.

У спина есть приятельское окно Спин размещается справа от приятельского окна

Спин размещается слева от приятельского окна При изменении позиции спина текст в при­ятельском окне меняется автоматически Разрешается использование клавиатур1»1 для изменения текущего состояния спина Спин располагается горизонтально Не отображать запятую для разделения классов в числах, отображаемых в приятель­ском окне

142

Таблица 34. Сообщения, посылаемые спину

Сообщение

Значение

Описание

UDM_SETRANGE UDM_GETRANGE

UDM_SETPOS UDM_GETPOS

UDM_SETRUDDY UDM GETBUDDY

WMJJSER-HOI WMJJSER+102

WMJJSER+103 WMJJSER+I04

WM_USER+l05 WM USER+I06

Установка диапазона прокрутки, wParam = О, min значение - в старшем слове IParam, max значение - в младшем слове IParam Получение диапазона прокрутки, wParam и IParam должны быть равны 0, возвращает max значение в младшем, a min значение - в старшем слове возвращаемого значения Установка текущей позиции спина, wParam~0, IParam - значение новой позиции Получение текущей позиции спина, wParam и IParam должны быть равны 0, возвращает текущую позицию в младшем слове во (вра­щаемого значения

Определение приятельского окна, wParam хэндлу приятельского окна, IParam - О, возвращает хэндл бывшего приятельского окна



Получение хэндла приятельского окна, wParam и IParam должны быть равны О, возвращает хзндл приятельского окна в младшем слове возвращаемого значения

Параметр dwStyle определяет стиль окна. Все стили, разработанные специально для спина, начинаются с UDS_. Они приведены в табл. 33.

Как и всегда, для управления спином используются сообщения. Обычно при нажатии одной из стрелок спина родительскому окну посы­лается сообщение WM_VSCROLL. При этом IParam этого сообщения содержит хэндл спина. При обработке этого сообщения необходимо быть аккуратным и ire забывать, что сообщения WM VSCROLL могут посту­пать и от других элементов управления.

Остальные сообщения, используемые спином, приведены в табл. 34.

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

#defmeIDM_F.xit l()l

#deime IDM_Pialog 102

#defineIDM About 103

^define ID OK 104

#define IDJIdit 105

fdefine ID Spin 106

Кроме этого, ниже приведен файл ресурсов, используемый

#include "spin.h"

SpinDemoMenu MENU

! POPUP "&File"

I

MENUITEM "E&xit", IDM Exit }

MENUITEM "&Dialog", IDM_Dialog

POPUP "&HcIp"

{ MENUITEM "&About", IDM_About

SpinDcmoDialog DIALOG 0, 0. 100, 100

STYLE DS_MODALFRAME | DS_3DLOOK | \VS_POPUP | WS_VISIBLE |

WS_CAPTION | WS_SYSMENU CAPTION "Spin Demo Dialog" FONT 8, "MS Sans Serif f

DEFPUSHBUTTON "OK". 1D_OK. 25, 60, 50, 14

CONTROL "", ID_Edit, "edit", ES LEFT | WSJTHILD j WS_VISIBLE |

WSJJORDER ! \VS_TABSTOP | ES_NUMBER, 25, 20, 50, 12

И наконец, непосредственно текст программы:

^include <windows.li> #includc <commctrl.h> ^include "spin.h"

HINSTANCE hlnst;

LRESULT CALLBACK SpinDemoWndProcfHWND, UINT, UINT, LONG ); BOOL CALLBACK SpinDcmoDialogProc(HWND. UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hinstancc, HINSTANCE hPrevInstance, LPSTR IpszCmdParam, int nCmdShow )



>

HWND liWnd;

WNDCLASS WndClass ;

MSG Msg;

char szClassNamef] = "SpinDcmo";

hlnst = hlnstance;

144

/* Registering our window class */ /* Fill WNDCLASS structure */

WndClass.stylc = CS_HREDRAW j CS_VREDRAW;

WndClass. IpfnWndProc = SpinDemoWndProc;

WndCiass.cbClsExtra = 0;

WndClass. cbWndExtra = 0;

WndCIass.hlnstance = hlnstance ;

WndClass.hlcon = Loadlcon (NULL,IDI_APPLICATION);

WndClass.hCursor = LoadCursor (NULL, IDC^ARROW);

WndClass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

WndClass. IpszMenuName = "SpinDemoMenu";

WndClass. IpszClassName = szCIassName;

if ( !RegisterClass(&WndClass) ) I

MessageBox(NULL,"Cannot register class", "Error", MB_OK); return 0;

hWnd = CreateWindow(szClassName, "Spin Demo",

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CWJJSEDEFAULT, CWJJSEDEFAULT, NULL, NULL, h!nstance,NULL); if(!hWnd) {

MessageBox(NULL,"Cannot create window", "Error",MB_OK); return 0;

InitCommonControlsO; /* Show our window */ ShowWindow(hWnd,nCmaShow); UpdateWindow(hWnd);

/* Beginning of messages cycle */

whilc(GetMessage(&Msg, NULL, 0, 0)) {

TranslateMcssage(&Msg); DispatchMcssage(&Msg); } return Msg.wParam;

LRESULT CALLBACK SpinDemoWndProc (HWND hWnd, UINT Message,

UINT wParam, LONG IParam ) I switch(Messagc)

145

case \VM_COMMAND: switch(wParam)

{ case IDM_Exil:

SendMessage(hWnd, WM_CLOSE, 0, 0);

break; case IDM_Dialog:

DialogBox(hInst, "SpinDemoDialog", hWnd, SpinDcmoDialogProc);

break;

i /

return 0;

case WM_DESTROY; PostQuitMessage(O); return 0;

I return DefWindowProc(hWnd,Message,wParam, IParam);

BOOL CALLBACK. SpinDemoDialogProc(HWND hDlg, UINT Message,

WPARAM wPararn, LPARAM IParam)

!

static HWND hEditWnd; static HWND hSpinWnd; switch(Message)

{

case WMJNITDIALOG: hEditWnd = GetDlgItem(hDlg,ID_Edit);

hSpinWnd = CreatcUpDownControl(WS_CHILD | WS_BORDER |

WS_VISIBLE ] UDS_SETBUDDYINT | UDS_ALIGNRIGHT, 0, 12,50,50, hDlg, ID_Spin, hlnst,



hEditWnd, 100,0,50);

return TRUE; case WM_COMMAND: switch(LOWORD(wParam))

{

case ID_OK: EndDialog(hDlg,0); return TRUE;

} break;

!

return FALSE;

Spin Demo Dialog



;.]50



Рис. 13. Диалоговое окно со спином

При выборе элемента «Dialog» из меню протраммы производится ото­ бражение диалогового окна, вид которого показан на рис. 13.

Действия, приводящие к отображению спина, локализованы в той части программы, которая обрабатывает сообщение WM_INITDIALOG. Спин создается посредством вызова функции CreateUpDownControl(), при этом в качестве приятельского окна указывается окно редактирования, созданное как часть ресурса с описанием диалогового окна. Все осталь­ные действия производятся автоматически. Читатель может попробовать изменить стили спина и посмотреть, к чему это приведет.

Т а б ,i и ц а 35. Стили трекбара

Слип,

Значение

 

Описание

 

TBSJTORZ

 

0x0000

 

Определяет горизонтальную ориентацию

 

 

 

 

 

т| скбара

 

TBSJiOTTOM

 

0x0000

 

Шкала расположена пол ползунком (для

 

 

 

 

 

горизонтального трекбара)

 

TBS_RIGHT

 

0x0000

 

Шкала расположена справа от трекбара (для

 

 

 

 

 

вертикального трекбара)

 

TBS AUTOTICKS

 

0x000 1

 

Шкала трекбара создается с делениями

 

TBS VERT

 

0x0002

 

Определяет вертикальную ориентацию

 

 

 

 

 

трекбара

 

TBS_TOP

 

0x0004

 

Шкала расположена над ползунком (для

 

 

 

 

 

горизонтального трекбара)

 

TBS_LEFT

 

0x0004

 

Шкала расположена слева от трекбара (для

 

 

 

 

 

вертикального трекбара)

 

TBS BOTH

 

0x0008

 

Шкала расположена с двух сторон трекбара

 

TBS NOTICKS

 

0x0010

 

Шкала трекбара создается без делений

 

TBS ENABLESELRANGE

 

0x0020

 

Разрешается отображение диапазона

 

TBS_FIXEDLENGTH

 

0x0040

 

При изменении диапазона длина трекбара не

 

 

 

 

 

изменяется

 

TBSJMOTHUMB

 

0x0080

 

У трекбара нет слайдера

 

<


147

РАБОТА С ТРЕКБАРОМ



Очередным клоном линейки прокрутки является ползунок (trackbar или slider). Его внешний вид достаточно эффектен и интересен. Он напо­минает регулятор, используемый в аппаратуре, скажем, в качестве регу­лятора громкости. Небольшим отличием трекбара от линейки прокрутки является то, что у ползунка есть шкала, вдоль которой он движется. Честно говоря, мне очень не нравится переводить на русский слова, к которым я уже привык и которые обычно используются в качестве про­граммистского сленга. Поэтому давайте будем в данном случае под словом «трекбар» понимать весь элемент управления, а под словом «слайдер» - указатель, движущийся вдоль шкалы.

К сожалению, для создания трекбара не предусмотрено специальной функции, поэтому создавать его необходимо посредством вызова функ­ции CreateWindowQ или CreateWindowExQ. При этом в качестве имени класса следует указать макрос TRACKBARjCLASS, который описан в commctrl.h:

#ifdef JATN32

«define TRACKBAR_CLASSA

#deime TRACKBAR_CLASSW

#ifdcf UNICODE

#define TRACKBAR_CLASS

tfdefine TRACKBAR_CLASS

#endif

#else

#defme TRACKBAR_CLASS

#cndif

"msctls_trackbar32" L"msclls trackbar32"

TRACKBAR_CLASSW TRACKBAR_CLASSA

"msctls trackbar"

При создании трекбара могут использоваться стили окна, идентифи­каторы которых начинаются с TBS (табл. 35).

Что еще можно сказать об этих стилях? По-моему, здесь все ясно. Да­же понятно, что стили TBS_HORZ, IBS BOTTOM и TBS RIGHT явля­ются стилями, принимаемыми по умолчанию.

А теперь настало время рассмотреть сообщения, посредством которых осуществляется управление трекбаром. Все эти сообщения приведены в табл. 36.

Таблица 36. Сообщения, посылаемые трекбару

Сообщение

Значение

Описание

TBM_GETPOS TBM^GETRANGEMIN TBM_GETRANGEMAX TBMJ3ETTIC

TBM_SETTIC TBM_SETPOS

TBM_SETRANGE

TBM_SETRANGEMIN TBM_SETRANGEMAX

TBM^CLEARTICS TBM_SETSEL

'BM_SETSELSTART "BM_SETSELEND BMJ3ETPTICS BM_GETTICPOS

WMJJSER WM_USER + I WMJJSER + 2 WM_USER + 3



WMJJSER + 4 WMJJSER + 5

WMJJSER + 6

WMJJSER + 7 WMJJSER + 8

WMJJSER + 9 WMJJSER + 10

WMJJSER + 11 WMJJSER+12 WMJJSER+14 WM USER+ 15

wParam и IParam = 0, возвращает текущую позицию слайдера

wParam и IParam = 0, возвращает нижнюю границу диапазона прокрутки wParam и IParam = 0, возвращает верхнюю границу диапазона прокрутки wParam = номер (от нуля) пометки, IParam = 0, возвращает позицию в диапазоне, соответствующую указанной пометке

wParam = 0, IParam = позиции, устанав­ливает пометку в указанной позиции wParam = TRUE, IParam = новой позиции слайдера, слайдер устанавливается в новую позицию

wParam = флаг перерисовки (BOOL), IParam = MAKELONG(wMinimum, wMaximum), устанавливает диапазон для слайдера трекбара

wParam — флаг перерисовки (BOOL), IParam = wMinimum, установка нижней

•раницы диапазона для слайдера wParam = флаг перерисовки (BOOL), IParam = wMaximum, установка верхней границы диапазона для слайдера wParam = флаг перерисовки (BOOL), IParam = 0, удаляет текущую пометку wParam = флаг перерисовки (BOOL), IParam = MAKELONG(wMinimum, «Maximum, установка диапазона выделения в трекбаре wParam = флаг перерисовки (BOOL), IParam = wStart, установка нижней границы выделения wParam = флаг перерисовки (BOOL), IParam = wEnd, установка верхней границы выделения wParam и IParam = 0, возвращает указа-

-ель на массив, содержащий позиции юметок

wParam = номер (от нуля) пометки, ^aram = 0, возвращает позицию пометки оконных координатах

149

Окончание табл. 36

Сообщение

Значение

Описание

TBM_GETNUMTICS TBM_GETSELSTART TBM_GETSELEND TBM_CLEARSEL

TBM_SETTICFREQ TBM SETPAGESIZE

TBM GETPAGESIZE

TBM SETLINESIZE

TBM GETLINESIZE

tbm getthumbrect tbm"getchannelrect tbm_setthumbrect tbm getthumbrect

WMJJSER + 16 WM_USER+ 17 WMJJSER + 18 WMJJSER + 19

WM_USER + 20 WM USER+ 21

WM USER+ 22

WM USER+ 23

WM USER + 24

WMJJSER + 25 WMJJSER + 26 WMJJSER + 27 WM USER + 28

wParam = 0, IParam - 0,возвращает

число пометок

wParam = 0, IParam = 0, возвращает

нижнюю границу выделения



wParam = 0, IParam - 0, возвращает

верхнюю границу выделения

wParam = флаг перерисовки (BOOL),

IParam ~~ 0, сбрасывает текущее

выделение

wParam = частоте следования пометок,

IParam = номеру позиции, начиная с

которой расставляются пометки,

устанавливает шаг расстановки

пометок

wParam ~ 0, IParam — wPageSize,

определяет, на сколько позиций

необходимо передвинуть слайдср при

получении сообщений

ТВ_PAGEDOWN и TBJ>AGEUP,

возвращает предыдущее значение

этого параметра

wParam = 0, IParam = 0, возвращает

значение параметра,о котором

говорится в предыдущей строке

таблицы

wParam — 0. IParam — wLineSize,

определяет, на сколько позиций

необходимо передвинуть слайдер при

получении сообщений

TB_LINEDOWN и TB_LINEUP,

возвращает предыдущее значение

этого параметра

wParam = 0, IParam •- 0, возвращает

значение параметра,о котором

говорится в предыдущей строке

таблицы

Т а б л ч ц а 37. Коды нотификации, посылаемые трекбаром

Сообщение

Описание

TB_LINEUP

TBJLINEDOWN

TB_PAGEUP

TBJ'AGEDOWN

TB_THUMBPOSITION

TB_THUMBTRACK TB_TOP

TB_BOTTOM ТВ ENDTRACK

Нажата VKJLEFT (стрелка влево) или VK_UP (стрелка вверх)

Нажала VK_RIGHT (стрелка вправо) или VK_DOWN (стрелка вниз) Нажата VKJNEXT (PageUp) или щелчок мытью перед елайдером Нажата VK_PRIOR (PageDown) или щелчок мышью после слайдера Слайдер зафиксирован после протяжки с помощью мыши

Слайдер протягивается с помощью мыши Нажата VK_HOME (Home), слайдер устанав­ливается в положение, соответствующее верхней границе диапазона Нажата VK_END (End), слайдер устанавлива­ется в положение, соответствующее нижней границе диапазона

Слайдер зафиксирован после перемещения с помощью клавиатуры

При манипуляциях с трекбаром последний посылает родительскому окну сообщения WM_HSCROLL. В младшем слове wParam содержится код нотификации, который определяет характер действия, произведенно­го с трекбаром. Старшее слово wParam содержит позицию слайдера в момент возникновения сообщения. Дескриптор окна трекбара записыва­ется в IParam. В табл. 37 приведены нотификационные сообщения, ис­пользуемые при работе с трекбаром.



Сообщениия WM TOP, WM BOTTOM, WM_LINEDOWN и WM LINEUP посылаются только в том случае, если пользователь воз­действует на трекбар с помощью клавиатуры, TB_THUMBPOSITION и TBJTHUMBTRACK посылаются в случае работы с мышью, остальные сообщения могут посылаться в обоих случаях.

Теперь, изучив теоретически работу трекбара, мы можем рассмотреть программу, в которой демонстрируются возможности этого элемента управления. Для нормальной работы программы помимо основного файла с текстом программы необходимы еще два файла: описаний и ресурсов. Файл описаний приведен ниже:

#с!еЛпе IDM_Exit 101

#define IDM_Dialog 102

ftk-fine IDM About 103

«define ID_OK 104 «define ID_Edit             105 «define ID_Spin             106 «define IDJTrackbar     107 А теперь - файл ресурсов. «include "trackbar.h" TrackbarMenu MENU { POPUP "&File"

{ MENUITEM "E&xit", IDM_Exit

}

MENUITEM "&Dialog", IDM_Dialog POPUP "&Help"

{ MENUITEM "&About", IDM_About

TrackbarDialog DIALOG 0, 0, 100, 100

STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE |

WS_CAPTION | WS_SYSMENU CAPTION "Trackbar Demo Dialog" FONT 8, "MS Sans Serif

{

DEFPUSHBUTTON "OK", ID_OK, 25, 73, 50, 14

CONTROL "", ID_Edit, "edit", ES_LEFT | WS_CHILD | WSJV1SIBLE |

WS_BORDER  WSJTABSTOP | ES_NUMBER, 25, 14, 50, 12

А теперь - очередь основного файла программы. «include <windows.h> «include <commctrl.h> «include "trackbar.h"

HINSTANCE hlnst;

LRESULT CALLBACK TrackbarWndProc(HWND, UINT, UINT, LONG ); BOOL CALLBACK TrackbarDialogProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hPrevInstance,

LPSTR IpszCmdParam, int nCmdShow ) {

HWND hWnd ;

WNDCLASS WndClass ;

MSG Msg;

char szClassName[] = "TrackbarDemo";

hlnst = hinstance; /* Registering our window class */ /* Fill WNDCLASS structure */

WndClass.stylc = CSJHREDRAW | CSJVREDRAW;



WndClass.lpfnWndProc = TrackbarWndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass. hinstance = hinstance ;

WndClass.hlcon = Loadlcon (NULL,IDI_APPLICATION); WndClass. hCursor = LoadCursor (NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); WndClass. IpszMenuName = "TrackbarMenu"; WndClass. IpszClassName = szClassName; if ( !RegisterClass(&WndClass) ) {

MessageBox(NULL, "Cannot register class", "Error", MB_OK);

return 0;

hWnd = CreateWindow(szClassName, "Trackbar Demo Program",

WS_OVERLAPPEDWINDOW, CWJJSEDEFAULT, CW_USEDEFAULT, CWJJSEDEFAULT, CWJJSEDEFAULT, NULL, NULL, h!nstance,NULL); if(!hWnd) {

MessageBox(NULL,"Cannot create window", "Error",MB_OK); return 0;

InitCommonControls(); /* Show our window */ ShowWindow(hWnd.nCmdShow); UpdateWindow(hWnd); /* Beginning of messages cycle */

while(GetMessage(&Msg, NULL, 0, 0)) {

TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam;

LRESULT CALLBACK TrackbarWndProc (HWND hWnd, UINT Message,

UINT wParam, LONG IParam ) {

switch(Message) {

case WM_COMMAND: switch(wParam)

i \

case IDM_Exit: SendMessage(hWnd, WM_CLOSE, 0, 0);

break;

case IDM_Dialog;

DialogBox(hInst, "TrackbarDialog", hWnd, TrackbarDialogPruc); break;

}

return 0;

case WMJDESTROY: PostQuitMessage(O); return 0;

return

DefWindowProc(h\Vnd,Mcssage,\vParam, IParam);

BOOL CALLBACK TrackbarDialogProc(HWND hDlg, UINT Message,

WPARAM wParam, LPARAM IParam)

{

static HWND hEditWnd; static HWND hSpinWnd; static HWND hTrackbarWnd; switch(Message) j

case WMJNITDIALOG: hEditWnd = GctDlgltemfliDlg, ID_Edit); hTrackbarWnd = GctDlgltem(hDlg, ID J'rackbar); hSpinWnd = CreateUpDownControl(WS_CHILD | WSJ3ORDER |

WS_VISIBLE j UDS_SETBUDDYINT | UDS_ALIGNRIGHT, 0, 12,50,50,

hDlg, !D_Spin, hlnst, hEditWnd, 10,0,5); hTrackbarWnd = CreatcWindow(TRACKBAR_CLASS,"Trackbar Demo",

WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_AUTOTICKS, 4,75, 142.40, hDlg, NULL, hlnst, NULL); SendMessagediTrackbarWnd. TBM_SETRANGE, TRUE,



MAKELONG(0,10));

SendMessagc(hTrackbarWnd. TBM^SETPOS, TRUE, 5); return TRUE; case WMJVSCROLL: SendMessagediTrackbarWnd, TBM^SETPOS, TRUE,

GetDlgIiemInt(hDlg, ID_Edit, NULL,!)); return TRUE; case WMJISCROLL: SetDlgltemlntOiDlg, ID_Edit, SendMcssage(hTrackbarWnd,

TBMJ3ETPOS, 0,0), TRUE); case WM_COMMAND: switch(LOWORD(wParam))

case ID OK:

EndDialog(hDlg,0); return TRUE;

break;

i i

return FALSE;

После того, как эта программа будет запущена и в основном меню программы будет выбран элемент «Dialog», на экране появится диалого­вое окно, вид которого показан на рис. 14.

Tiackbar Demo DialogQ!



,__...

Рис. 14. Диалоговое окно со спином и трскбаром

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

При написании этой программы использовались две новые функции. Первая, GetDlgItemInt(), описана в файле winuser.h следующим образом:

WINUSERAPI UINT WINAPI GetDlgItemInt(HWND hDlg,     int nIDDlgllem,

BOOL «IpTraiislated. BOOL bSigned);

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

Вторая функция, описанная в том же файле, имеет следующий прототип:

WINUSERAPI BOOL WINAPI SetDIg[temTextA(HWND hDlg. int ninDlgltem,

LPCSTR IpString); WINUSERAPI BOOL WINAPI SetDlgItcmTcxtW(HWND hDlg. int nlDDlgltcm,

LPCWSTR IpString);

#ifdefUNICODE

tfdefine SetDlgltemText SetDlgltemTextW

#e!se

#defme SetDlgltemText SetDlgltemTextA

#endif//! UNICODE

Она производит действие, обратное GetDlgItemInt(), т. е. получает в качестве аргумента целое число и возвращает его представление в виде строки. В программе она используется для того, чтобы в окне редактиро­вания отобразить номер позиции слайдера. Все просто, не так ли?



РАБОТА С ИНДИКАТОРОМ (PROGRESS BAR'OM)



Надеюсь, что читатель уже имеет опыт инсталляции программных продуктов для Windows. Там степень завершенности задачи отражается синей полосой, которая постоянно растет. По достижении ею отметки, соответствующей 100%, процесс инсталляции оказывается завершенным. Вот эта синяя полоса и является индикатором, который индицирует степень завершенности достаточно длительной задачи.

По своему внешнему виду это, наверное, самый простой из общих элементов управления. То же самое можно сказать и о работе с ним. У него только один стиль, и всего пять сообщений применяются для управ­ления им.

Как и в случае трекбара, специальной функции для создания индика­тора нет. Для создания индикатора необходимо использовать функцию CreateWindowQ или CreateWindowExQ. Для указания имени класса необходимо использовать макрос PROGRESS_CLASS, который в файле commctrl.h описан следующим образом:

#ifdef_WIN32

tfdefme PROGRESS_CLASSA

#define PROGRESS_CLASSW

#ifdef UNICODE

#defme PROGRESS_CLASS

#else

tfdefme PROGRESSJXASS

#endif

#else

#define PROGRESS_CLASS

#endif

"msctls_progress32" L"msctls_progress32"

PROGRESS_CLASSW PROGRESS_CLASSA

"msctls_progress"

156

Для управления индикатором используются сообщения. Поговорим о них.

Наверное, для того чтобы использовать индикатор, необходимо опре­делить для него минимальное и максимальное значения (в приведенном примере, когда я говорил об инсталляционных программах, минимальное и максимальное значения равны 0 и 100 соответственно). С этой целью используется сообщение PBM_SETRANGE (WM_USER+1). wParam его должен быть равно 0, a IParam должно определяться как MAKELONG(wMinRange, wMaxRange), где wMinRange и wMaxRange -минимальное и максимальное значения. Функция, пославшая это сооб­щение, возвращает значения старого диапазона. Если обозначить возвра­щаемое значение как IReturn, то LOWORD(lReturn) будет определять нижнюю границу диапазона, a HIWORD(lReturn) будет содержать верх­нюю границу диапазона.



Для установки индикатора в определенную позицию (другими слова­ми, для определения необходимой длины индикатора) используется сообщение PBM_SETPOS (WMJJSER + 2). При этом wParam должен содержать позицию, в которую необходимо установить индикатор. IParam должен быть равным 0. Предыдущая позиция индикатора возвра­щается функцией, пославшей сообщение.

Сообщение РВМ DELTAPOS применяется для определения значения, на которое будет увеличена длина индикатора. wParam этого сообщения определяет приращение, a IParam должен быть равным 0. Функция, пославшая сообщение, возвращает значение предыдущей позиции.

Для определения шага, с которым будет увеличиваться длина индика­тора, используется сообщение PBM^SETSTEP, wParam которого опреде­ляет шаг, a IParam должно быть равно нулю. По умолчанию, шаг прира­щения равен 10.

Сообщение РВМ STEPIT указывает индикатору, что необходимо осуществить увеличение длины, используя при этом все текущие уста­новки, т. е. текущую позицию и текущий шаг. Оба параметра сообщения должны быть равны 0.

И, как всегда, демонстрационная программа, с помощью которой читатель сможет увидеть управление индикатором в действии.

Вся программа состоит из трех файлов. Первый - файл заголовков:

fldefinelDC MSCTLS TRACKBARI       101

Adeline IDFvf Exit 101

#dcfme IDM_Dialog 102

#definc IDM_About 103

#defincID_OK. 104

#dcilne ID_Cdit 105

#deiine ID_Spin 106

#define ID^ProgressBar 107

Второй файл - файл ресурсов, он также приводится ниже.

#include "ProgressBar.h"

ProgrcssBarMenu MENU

{

POPUP "&File"

!

MENUITEM "E&xit", IDM Exit

MENUITEM "&Dialog", IDM_Dia!og

POPUP "&Help"

{

MENUITEM "&About", IDM_About

ProgressBarDialog DIALOG 0, 0, 100, 100

STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WSJVISIBLE |

WS_CAPTION i WS_SYSMENU CAPTION "Progrcssbar Demo Dialog" FONT 8, "MS Sans Serif

{

DEFPUSHBUTTON "OK", ID_OK, 25, 73, 50. 14



CONTROL "", ID_Edit, "edit", ES_LEFT | WS_CH1LD | WS_VISIBLE [

WS_BORDER | WS_TABSTOP | HS_NUMBER, 25, 14, 50. 12 >

И, естественно, основной файл программы.

#include <windows.h>

#include <commctrl.h>

#include "ProgressBar.h"

HINSTANCE hlnst;

LRESULT CALLBACK ProgressBarWndProc ( HWND, UINT, UINT, LONG ); BOOL CALLBACK ProgressBarDialogProc(I!WND, UINT, WPARAM,

LPARAM);

int WINAPI WinMain ( HINSTANCE hinstancc, HINSTANCE hPrcvInstancc, LPSTR IpszCmdParam, int nCmdShow )

HWND hWnd ;

WNDCLASS WndClass ;

MSG Msg;

char szClassNamef] = "ProgressBarDemo"

hlnst = hinstancc;

/* Registering our window class */ /* Fill WNDCLASS structure */

WndClass.stylc = CS_HREDRAW j CS_VREDRAW;

WndClass.IpfhWndProc = ProgressBarWndProc;

WndClass.cbClsExtra = 0;

WndClass.cbWndExtra = 0;

WndClass.hlnstance = hlnstance ;

WndClass.hlcon = Loadlcon (NULL,!DI_APPLICATION);

WndClass.hCursor = LoadCursor (NULL, 1DC_ARROW);

Wndclass.hbrBackground - (HBRUSH) GetStockObject (WHITE_BRUSH);

WndClass.IpszMenuNamc = "ProgressBarMenu";

WndClass. IpszClassName = szClassName;

if ( !RegistcrClass(&WndClass)) {

MessageBox(NULL,"Cannot register cIass","Error",MB_OK); return 0;

} hWnd = CrealeWindow(szClassName, "Progressbar Demonstration Program",

WS_OVERLAPPED\VINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, Mnstance,NULL); if(!hWnd) {

MessageBox(NULL,"Cannot create window","Error",MB_OK); return 0;

/* Show our window */ ShowWindow(hWnd,nCmdShow); Update Window(hWnd);

/* Beginning of messages cycle */

while(GetMessage(&Msg, NULL, 0, 0)) {

TranslateMessagc(&Msg); DispatchMessage(&Msg); ( return Msg.wParam;

LRESULT CALLBACK ProgressBarWndProc (HWND hWnd, UINT Message,

UINT wParam, LONG IParam )

j t

swilch(Mcssage) {

case WM_COMMAND; swilch(LOWORD(wParain))

159

case IDM_Dialog:

DialogBoxfhlnst, "ProgressBarDialog", liWnd, ProgressBarDialogProc);



break; case IDMJExit:

SendMessage(hWnd, WM_CLOSE, 0,0);

break;

}

return 0;

case WM_DESTROY: PostQuitMessage(O); retuni 0;

! return DefWindowProc(hWnd,Message,wParam, IParam);

BOOL CALLBACK ProgressBarDialogProc(HWND hDlg, UINT Message,

WPARAM wParam, LPARAM IParam)

{

static HWND hEditWnd; static HWND hSpinWnd; static HWND hProgressBarWnd; int i; switch(Message)

{

case WMJNITDIALOG: hEditWnd = GetDlgItem(hDlg, ID_Edit);

hSpinWnd = CreatcUpDownControl(WS_CHILD | WS_BORDER |

WS_VISIBLE | UDS_SETBUDDYINT | UDS_ALIGNRIGHT, 0, 12,50,50, hDlg, ID_Spin, hlnst, hEditWnd, 10,0,5);

hProgressBarWnd = CreateWindow(PROGRESS CLASS,

"ProgressBar Demo", WS_CHILD j WSJVISIBLE, 10,75, 130,20, hDlg, NULL, hlnst, NULL); SendMessage(hProgressBarWnd, PBM^SETRANGE, 0,

MAKELPARAM(O.IO));

SendMcssage(hProgressBarWnd, PBM_SETSTEP, 1, 0); SendMessage(hProgressBarWnd, PBM_SETPOS, 5, 0); return TRUE; case WM_VSCROLL: SendMessage(hProgressBarWnd, PBM_SETPOS, GetDlgItemInt(hDlg,

ID_Edit,NULL,l),0); return TRUE; caseWM COMMAND:

160

switch(LOWORD(wParam))

{

case ID_OK: EndDialog(hDlg,0); return TRUE; } break;

} return FALSE;

Вид диалогового окна, возникающего после выбора пользователем эле­мента «Dialog» в главном меню программы, показан на рис. 15.

Piogiessbai Demo .

4XI

 

 

 

,  :  - •' •'

 

"-- ...:.,:.'4i.;-:;;v,W>»i';..-';.v"

---::     \: !^"i';,V;"«Tv::,«™. • ,,.-   ,',   •   -.;•:•*?-   •-•>-/.:

 

cm --..-'

 

••••••

 

:}'--

 

;.                  r.......0.____,:||

 

•i'ij"-.

 

 

 

V^-V""V.

 

Рис. 15. Диалоговое окно со спином и индикатором

Эта программа по своему действию очень похожа на приведенную в предыдущем разделе. Разница состоит в том, что спин управляет не трекбаром, а индикатором. Рекомендую читателю нажать несколько раз кнопки спина для того чтобы позиция спина изменилась, и посмотреть, что произойдет с индикатором. При разборе программы особое внимание следует уделить обработке сообщений WMJNITDIALOG и WM_VSCROLL.



Далее рассмотрим окна подсказок (Tooltip Controls) и списки изобра­жений (ImageLists). При изучении Win32 я не увидел тех моментов, когда два этих элемента управления использовались бы самостоятельно. Они являются только вспомогательными элементами.

РАБОТА С ОКНАМИ ПОДСКАЗОК



Окна подсказок - это небольшие всплывающие окна, которые содер­жат одну строку текста, объясняющую назначение какого-либо инстру­мента (tool) родительского окна. Под инструментом в данном случае понимается либо элемент управления, присутствующий в родительском

161

окне (пример - полоса инструментов в WinWord'e), либо прямоугольная область внутри рабочей области окна.

Окна подсказок почти всегда скрыты. Отображаются они только в том случае, если пользователь задерживает курсор мыши на инструменте. Подсказка появляется рядом с инструментом и исчезает тогда, когда пользователь либо нажимает клавишу (мыши или клавиатуры), либо убирает курсор с инструмента. Одна подсказка может появляться при задержке курсора на разных инструментах.

К сожалению, не все общие элементы управления имеют специальную функцию для своего создания ( © ). Окна подсказок тоже создаются только посредством применения CreateWindow() или CreateWindowExQ. В этом случае для их создания необходимо использовать макрос TOOLTIPS^CLASS, который в файле commctrl.h описан следующим образом:

#ifdef_WIN32

^define TOOLTIPS_CLASSW

#defmc TOOLTIPS_CLASSA

#ifdef UNICODE

ftleflne TOOLTIPS_CLASS

#else

#define TOOLTI PS CLASS

#cndif

#clse

#define TOOLTIPS (CLASS

#endif

L"tooltips_class32" "tooltips_class32"

TOOLTIPS CLASSW TOOI.TIPS_CLASSA

"tooltips_class"

При создании подсказки могут использоваться два стиля, специально разработанные для окон этого типа - TTS_ALWAYSTIP и TTS_NOPREFIX. Подсказка, имеющая стиль ITS ALWAYSTIP, появля­ется при помещении курсора на инструмент вне зависимости от того, активно или не активно родительское окно.

Необходимо отметить еще одну возможность создания окон подска­зок. Дело в том, что в Win32 некоторые элементы управления имеют специальный стиль, обычно оканчивающийся на TOOLTIPS. Он позво­ляет программе не создавать собственные окна подсказок, а использовать встроенные возможности системы. Разумеется, этот способ использова­ния подсказок намного проще.



Достаточно часто подсказки используются в панели инструментов, при этом каждая кнопка в панели инструментов соответствует элементу

162

меню. При этом подсказки, как правило, совпадают с текстом, отобра­жаемым в соответствующем элементе меню. Если окно подсказки созда­ется со стилем TTS_NOPREFIX, то система автоматически удаляет знак амперсанта, если он присутствует в строке меню.

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

Т а б л и ц а 38. Сообщения, посылаемые "подсказкам"

С сюощсшк'

Описание

ТТМ_ ACTIVATE ТТМ SETDI-LAYTIMF.

ТТМ ADDTOOL TTM_DELTOOL

TTM_NEWTOOLRECT TTM_RELAYEVENT

ТТМ GF.TTOOLINFO TTM_SETTOOLINFO ТТМ НГГП-ST

ТТМ GF.TTEXT TTMJJPDATETIPTEXT TTMJiETTOOLCOUNT ТТМ  ENUMTOOLS

TTM_GETCURRENTTOOL ТТМ WINDOWFROMPOINT

WM USER

WM USER + 3

WM USER • 7

WM USER ~  13

WM USER + i 6

Подсказка делается активной или неактивной. wParam = TRUE - под-ска зка активна, IParam всегда = О Определяются интервал инициализа­ции, интервал отображения и интервал повторного отображения Регистрирует инструмент, с которым будет работать подсказка Удаляет ранее добавленный инстру­мент

Определяет границы окна подсказки Передаст сообщение от мыши окну подсказки для обработки Запрос информации об инструменте, с которым работает подсказка Установка информации для инстру­мента

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

Получение текста подсказки, отображаемой с инструментом Установка текста подсказки для инструмент

Получение числа инструментов, с которыми работает подсказка Позволяет программе последова­тельно перебрать все инструменты, с которыми работает окно подсказки Получение информации о текущем инструменте

Выдача окна подсказки в месте, определяемом не положением курсора, а параметрами сообщения



Так как подсказка является окном, то для управления ею используют­ся сообщения. Их список сообщений с кратким описанием приведен в табл. 38. Рассмотрим наиболее часто используемые сообщения.

Для того чтобы подсказка работала с тем или иным инструментом, не­обходимо этот инструмент включить в список инструментов, с которыми работает окно подсказки. Для этого окну подсказки надо направить сообщение TTM_ADDTOOL. WParam этого сообщения должен быть равен 0, a IParam содержать указатель на структуру типа TOOLINFO. Эта структура описана в файле commctrl.h :

typedef struct tagTOOLINFOA {

UINT cbSize;

UINT uFlags;

HWND hwnd;

UINT uld;

RECT reel;

HINSTANCE hinst;

LPSTR IpszText; } TOOLINFOA, NEAR «PTOOLINFOA, FAR «LPTOOLINFOA;

typedef struct tagTOOLINFOW {

UINT cbSize;

UINT uFlags;

HWND hwnd;

UINT uld;

RECT rcct;

HINSTANCE hinst;

LPWSTR IpszText; } TOOLINFOW, NEAR *PTOOLINFOW, FAR *LPTOOLINFOW;

#ifdef UNICODE tfdcfine TOOLINFO

#define PTOOLINFO

#defme LPTOOLINFO

#else

tfdefine TOOLINFO tfdefine PTOOLINFO tfdefine LPTOOLINFO

#endif

TOOLINFOW PTOOLINFO W LPTOOLINFOW

TOOLINFOA PTOOLINFOA LPTOOLINFOA

Первое поле этой структуры - cbSize - должно содержать размер в байтах структуры типа TOOLINFO. Сам факт присутствия поля, содер­жащего такую информацию, говорит о том, что фирма Microsoft не исключает возможности изменения и/или дополнения этой структуры.

164

Таблица 39. Битовый файлы, определяющие вил и поведение подсказки

Фл;и

Значение

 

Описание

 

TTFJDISHWND

TTF CENTERTIP TTF_RTLREADING

 

0x0 1

0x02 0x04

 

Флаг установлен - поле uld содержит хэндл инструмент;!, иначе - идентификатор инструмента Центрирует подсказку под инструментом Отображает текст справа налево, как в арабском

 

 

 

 

 

Я'|ЫКС

 

TTF_SUBCLASS

 

Ох 10

 

Подсказка должна перехватывать сообщения WM MOUSEMOVE, адресованные инструменту

 

Следующее поле - uFlags - содержит флаги, определяющие внешний вид и поведение подсказки, а также представление информации в других полях этой структуры. Возможные флаги приведены в табл. 39.



Поле hwnd определяет родительское окно инструмента.

Поле uld обычно содержит идентификатор инструмента. Если по­ле uFlags включает TTF_IDISHWND, то поле uld содержит хэндл окна, внутри которого находится область, используемая в качестве инструмента.

Следующее поле - reel - определяет координаты окна инструмента от­носительно левого верхнего угла клиентской области окна, определяемо­го полем hwnd. Если поле uFlags включает флаг TTF_IDISHWND, поле rect игнорируется.

В поле hinst хранится хэндл экземпляра программы, которая со­держит строковый ресурс, определяющий текст подсказки. Если это поле не равно нулю, то поле IpszText содержит идентификатор стро­кового ресурса.

Как, надеюсь, читатель уже понял, поле IpszText может интерпрети­роваться по-разному. Итак, вариант первый. Если значение поля IpszText равно LPSTR TEXTCALEBACK, то именно окно, хэндл которого указан в поле hwnd, получает нотификационное сообщение TTN__NEEDTEXT, уведомляющее о том, что родительское окно инструмента должно опре­делить, какой текст подсказки должен быть отображен. Второй вариант -поле содержит идентификатор строкового ресурса, в котором определен текст сообщения. Этот вариант используется тогда, когда поле hinst не равно 0. Кроме этого, признаком того, что поле содержит идентификатор строкового ресурса, является старшее нулевое слово. И, наконец, третье, наиболее часто использующееся поле, содержит указатель на строку, содержащую текст подсказки.

Таким образом, мы видим, что структура типа TOOLINFO полностью определяет, что используется в качестве инструмента - элемент управле­ния или область экрана, а также где находится текст подсказки - в ресур­сах, в строке программы или он определяется родительским окном инст­румента.

В любой момент программа может изменить текст подсказки. Для этого окну подсказки посылается сообщение ТТМ UPDATETIPTEXT, wParam которого должен быть равным 0, a IParam - указывать на структу­ру типа TOOLINFO.

Программа может получить текст, который используется для выдачи подсказки об инструменте с помощью посылки окну подсказки сообще­ния ТТМ GETTEXT. В этом сообщении wParam должен быть равным 0. IParam, как и в предыдущем случае, должен содержать указатель на структуру типа TOOLINFO, в которой определяется инструмент, под­сказка о котором запрашивается. Ноле IpszText указывает на буфер, в который будет записан текст подсказки.



Для того чтобы отобразиться, окно подсказки должно получить сооб­щение от мыши. Так как Windows посылает сообщения только тому окну, поверх которого находится курсор, программа должна использовать сообщение TTM_R.ELAYEVENT для того чтобы транслировать сообще­ние окну подсказки. wParam этого сообщения должен быть равным нулю, a IParam должен содержать указатель на структуру типа MSU, в которой хранится информация о транслируемом сообщении. При этом необходи­мо учесть, что окно подсказки обрабатывает информацию только о сообщениях, приведенных ниже: WM IJiUTTONDOWN;

" WM LBUTTONUP;

* WM]MUUTTONI)OWN: ]WM MBUTTONUP;

* WM^MOUSEMOVF;

* WM RBUTTONDOWN;

* WM RBUTTONUP.

Если инструмент представляет собой прямоугольную часть окна, то тогда никаких сложностей не появляется. Если же инструмент является системным окном (таким, например, как кнопка), то в этом случае возни­кают определенные трудности. Программа должна будет или перехваты­вать сообщения посредством использования hook'oB, или подменить оконную функцию системного окна (осуществить subclassing). К сожале­нию, рассмотрение вопросов, связанных с поок'амн и subclassing'oM выходит за рамки этой книги, поэтому я вынужден буду остановиться только на подсказках, связанных с областью окна. Уважаемый читатель!

166

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

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

При работе окно подсказки использует три определенных временных интервала. Первый из них, называемый интервалом инициализации, определяет период, в течение которого курсор должен находиться в пределах интересующей области для того, чтобы отобразилась закладка. Второй - интервал повторного отображения - определяет задержку между последовательными отображениями окна подсказки в тех случаях, когда курсор скользит по инструментам, например, по панели инструментов. Третий интервал - интервал отображения - определяет время, в течение которого подсказка находится на отображении в тех случаях, когда курсор находится внутри интересующей области или в преде­лах границы инструмента. Все чти периоды могут быть определены с помощью сообщения ТТМ SETDELAYT1ME. wParam этого сообще­ния определяет, какой интервал устанавливается. IParam определяет длительность интервала в миллисекундах. Допустимые значения wParam приведены в табл. 40.



В тол) случае, когда инструментом является область окна, размеры и/или положение которой изменились, окну подсказки необходимо послать сообщение ТТМ NEWTOOLRECT для того, чтобы подсказка появлялась в нужном месте. wParam этого сообщения всегда равен 0.

Тай .1 и и а 40 Идентификаторы временных интервалов

П„р

 

Значение

 

Описание

 

TTDT AUTOMATIC

TTDT RP.SHOW ТТГУГ"ЛЬ'ТОРОР TTDT INITIAL

 

0 1

 

Все интервалы вычисляются автоматически на основе IParam Определяется интервал повторного отображения Определяется интервал отображения Определяется интервал инициализации

 

167

IParam этого сообщения должен указывать на структуру типа TOOLINFO, поля hwnd и uld которой должны определять инструмент, а поле rect - новые границы инструмента. В том случае, когда инструмент реализован как окно, информировать подсказку о его изменении не нужно, так как подсказка определит факт нахождения курсора в границах инструмента по хэндлу окна.

Перед своим отображением окно подсказки посылает родительскому окну нотификационное сообщение TTN_SHOW, а перед скрытием -TTN_POP. В данном случае нотификационные сообщения посылаются с помощью сообщения WM NOTIFY.

Для получения информации об инструменте программа может ис­пользовать сообщения TTM_GETCURRENTTOOL и TTMJ3ETTOOLINFO. Изменить информацию об инструменте можно с помощью сообщения TTM_SETTOOLINFO. Если программе требуется, чтобы подсказка с данным инструментом больше не работала, окну подсказки нужно направить сообщение ТТМ DELTOOL. Параметры этих сообщений однотипны и ничего сложного в них нет. Рекомендую читателю изучить работу этих сообщений самостоятельно.

Как уже было сказано, сами по себе окна подсказок не используются, поэтому демонстрационной программы я не приведу. Тем не менее, в разделе, посвященном закладкам, будут даны примеры другого способа использования подсказок, применимого, к сожалению, только к общим элементам управления, которые появились в Windows NT и Windows'95. В чем состоит этот способ? Дело в том, что при создании некоторых элементов управления можно указать стиль, позволяющий этим окнам реагировать на сообщения WMJNOTIFY с нотпфикационным кодом TTNJNEEDTEXT. Скажем, для закладок этот стиль называется TCSJTOOLTIPS, для панели инструментов - TBSJTOOLTIPS и т. д. В этих случаях родительскому окну элемента управления в качестве IParam сообщения WM_ NOTIFY передается указатель на структуру типа TOOLTIPTEXT, описание которой, находящееся в файле commctrl.h имеет следующий вид:



typcdef struct tagTOOLTIPTEXTA {

NMHDR heir;

LPSTR ipszTcxt;

char s/Tcxt[8()];

HINSTANCE hinst;

UINT u Flags; ! TOOLTIPTEXTA, FAR «LPTOOLTIPTEXTA;

typcdef struct tagTOOLTIPTEXTW {

NMHDR hdr;

LPWSTR IpszText;

WCHAR szText[80];

HINSTANCE hinst;

UINT uFIags; ) TOOLTIPTEXTW, FAR *LPTOOLTIPTEXTW;

#ifdcf UNICODE

#define TOOLTIPTEXT Wcllne LPTOOLTIPTEXT

#else

tfdefmc TOOLTIPTEXT

#definc LPTOOLTIPTEXT

#endif

TOOLTIPTEXTW LPTOOLTIPTEXTW

TOOLTIPTEXTA LPTOOLTIPTEXTA

Первое поле этой структуры, тоже структура, но типа NMHDR, опи­сана в файле winuser.h так:

typcdef struct tagNMHDR

i

HWND hwndFrom;

UINT idFrom;

UINT code;         //NM_code }   NMHDR; typedef NMHDR FAR * LPNMHDR;

hwndFrom - хэндл элемента, пославшего нотификационное сообщение, idFrom - идентификатор этого элемента, code - код нотификационного сообщения. Вроде бы все ясно.

Второе поле стуктуры типа TOOLTIPTEXT (как бы не запутаться с этими структурами!) - IpszText - может содержать указатель на строку, выдаваемую в качестве подсказки, или, если поле hinst не равно 0, содер­жит идентификатор строкового ресурса, определяющего текст выдавае­мой подсказки.

Вместо того чтобы определять указатель на строку, можно скопиро­вать эту строку в буфер szText, который является третьим полем структу­ры типа TOOLTIPTEXT.

Поле hinst является хэндлом экземпляра, содержащего строковый ре­сурс с текстом подсказки. Если IpszText является указателем на строку подсказки, это поле должно быть равным NULL.

И наконец, последнее поле - uFIags - содержит комбинацию флагов TTFJDISHWND и TTF RTLREADING, которые были рассмотрены ранее.

Анализируя поля этих двух структур, можно определить элемент, для которого определяется подсказка. Для того чтобы эта подсказка появи-лась на экране, достаточно определить либо указатель на строку подсказ­ки, либо ее идентификатор в таблице строк (не забыть при этом о поле hinst!), либо скопировать эту строку в предлагаемый буфер. И все! При­мер подобного использования подсказок приведен в разделе о работе с закладками.



РАБОТА СО СПИСКОМ ИЗОБРАЖЕНИЙ



И Windows предусмотрен интересный элемент, который лично я могу назвать элементом управления с большой натяжкой. Тем не менее, этот элемент активно используется при работе с другими элементами управ­ления, например, с закладками, речь о которых еще впереди. Я имею в виду список изображений (Image List). Он представляет собой коллекцию изображении одинакового размера, к каждому из которых можно осуще­ствить доступ по его индексу. Список изображении используется для эффективного управления и манипулирования большими наборами изображении.

Наверное, этот элемент управления самостоятельно не используется еще и потому, что он не является окном. Список изображений - это всего-навсего структура в памяти, обеспечивающая простой доступ к изобра­жениям.

Как и в случае обычных списков, при работе со списками изображе­ний можно создавать и удалять списки, добавлять, удалять и изменять элементы списков. Специфическим именно для списков изображений являются операции по прорисовке и перетаскиванию изображений.

Списки изображений могут быть немаскированными и маскирован­ными. Немаскированный список представляет собой один большой цветной bitmap, который, в свою очередь, состоит из одного или несколь­ких изображений. Маскируемый список состоит из двух больших bitmap'oB, первый из которых, цветной, содержит непосредственно список изображений, а второй, монохромный, содержит список масок -по одной для каждого элемента.

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

Для создания списка изображений приложению необходимо вызвать функцию ImageList Create(), которая описана в файле commctrl.h сле­дующим образом:

WINCOMMCTRLAPI HIMAUEL1ST WINAM

lst Crcatc(int ex. int су,

UfNT Hags. int cliiitial, inl cGrow):



G этой функции первые два аргумента, сх и су, определяют размер в пикселах каждого изображения. Третий аргумент, flags, указывает тип списка изображений. Для каждого типа в файле commctrl.h предусмотрен. макрос, начинающийся с ILC . Список этих типов приведен в табл. 41.

Размер создаваемого bitmap 'а вычисляется, исходя из значения четвертого аргумента. Этот аргумент определяет, сколько изображений должен включать bitmap. Если на каком-то этапе число изображений, включенных в bitmap, достигнет предельного значения, то система авто­матически расширит bitmap, добавив место для хранения еще определен­ного числа изображений, которое определяется последним параметром функции - cGrow.

Т а Я л и ц а 41. Флаги, используемые при создании списка изображении

 

Флаг

 

Значение

 

Описании

 

 

 

ILC COLOR

 

0x1)000

 

Используется флаг ни умолчанию, обычно

 

 

 

 

 

 

 

ILC COLOR4. для старых драйверов -

 

 

 

 

 

 

 

ILC COLORDDH

 

 

 

ILC MASK

 

0x0001

 

Создается маскированный bitmap

 

 

 

ILC_COLORDDO

 

OxOOFE

 

Используется bitmap, зависящий от устройства

 

 

 

 

 

 

 

(устаревший формат)

 

 

 

ILC_COI.OR4

 

O.\0004

 

U качестве bitmap 'а. содержащею изображения,

 

 

 

 

 

 

 

используется 16-цвстнын bitmap

 

 

 

ILC COLOR»

 

0x0008

 

И качестве bitmap'a, содержащего изображения.

 

 

 

 

 

 

 

используется 256-цветный bitmap

 

 

 

II. С COLOR 1П

 

0x0010

 

I) качестве bitmap'a. содержащего изображения.

 

 

 

 

 

 

 

используется bitmap, допускающий одновременное

 

 

 

 

 

 

 

использование до 65536 цветок

 

 

 

If С COLOR24

 

0x0018

 

В качестве bitmap'a, содержащего изображения,

 

 

 

 

 

 

 

используется bitmap, допускающий до 2**24 цветов

 

 

 

ILC COLOR32

 

ОхП('.20

 

Н кичестнс hitmap'a. содержащего изображения.

 

 

 

 

 

 

 

нсполмусгси bitmap, допускающий до 2**J2 цвет он

 

 

 

ILC I'ALETIl-

 

ОхОЙОО

 

Со списком изображений используется цнстовая

 

 

 

 

 

 

 

палитра

 

<


170

171

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

Но что же происходит при создании списка изображений? Помимо создания непосредственно bitrnap'a с указанными характеристиками, функция создает контекст, совместимый с экраном, и выбирает создан­ный bitmap в качестве текущего для этого контекста. В случае маскиро­ванного bitmap'a функция создает два экранно-совместимых контекста, при этом для одного в качестве текущего она выбирает bitmap с изобра­жениями, а для другого - bitmap с масками.

Естественно, что при использовании такой техники резко уменьшает­ся время, необходимое для копирования изображения на экран. Как результат - программы, использующие список изображений, могут работать значительно быстрее тех, которые перед использованием выну­ждены загружать изображения из ресурсов.

Для того чтобы удалить список изображений из памяти (отдельные изображения, из которых состоит список, остаются на своих местах, уничтожаются только указатели, что, собственно, и делает набор изобра­жений списком), необходимо вызвать функцию ImageList_Destroy(), которая описана следующим образом:

WINCOMMCTRLAPI BOOL WINAPI ImageList_Dcstroy(HIMAGELIST himl);

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

Итак, надеюсь, читатель понял, что такое список изображений и для чего он служит. Теперь возникают очередные вопросы: как добавлять изображения в список, удалять их и производить манипуляции с ними?

Для того чтобы добавить изображение в список, необходимо вос­пользоваться функцией ImageList_Add(), описание которой имеет следующий вид:

WINCOMMCTRLAPI int WINAPI ImageList_Add(HIMAGELlST him!,

HBITMAP hbmlmage, HBITMAP hbmMask);

Первый аргумент этой функции - himl - очевиден: хэндл списка изо­бражений. Второй аргумент - hbmlmage - представляет собо хэндл добав­ляемого в список изображения. Третий аргумент - hbmMask - хэндл



монохромного bitmap'a, который содержит маски. В случае немаскиро­ванного списка третий аргумент игнорируется.

Описание функции ImageList_AddMasked() приведено ниже:

WINCOMMCTRLAPI int WINAPI ImageList_AddMasked(HIMAGELIST himl,

HBITMAP hbmlmage, COLORREF crMask);

Эта функция действует почти так же, как и предыдущая, но маска ге­нерируется автоматически. Для генерации маски необходимо задать цвет. Если в изображении встречается пиксель указанного цвета, то цвет этого пикселя заменяется на черный, а соответствующий бит маски - на 0. В результате, при прорисовке изображения пиксели указанного цвета становят­ся прозрачными. Аргументы этой функции также очевидны. Первый - хэндл списка изображений, второй - хэндл включаемого в список изображения, третий - цвет пикселей, которые необходимо сделать прозрачными, при прорисовке.

Для добавления в список иконки или курсора используется макрос ImageList_AddIcon(), первым аргументом которого необходимо указать хэндл списка изображений, а вторым - хэндл добавляемой иконки или добавляемого курсора. Макрос возвращает индекс добавленного изобра­жения.

При необходимости программа может создать новую иконку или кур­сор, используя изображение и маску из списка изображений. Для этой цели необходимо использовать функцию ImageList_GetIcon(). Её описа­ние приведено ниже:

WINCOMMCTRLAPI HICON WINAPI ImageList_GetIcon(HIMAGELIST himl,

int i, UINT nags);

Первый аргумент этой функции - хэндл списка изображений. Второй -индекс изображения, на основе которого будет создана иконка или кур­сор. Третий аргумент - флаги прорисовки, которые можно найти в табли­це, приведенной при описании функции ImageList_Draw().

Функция возвращает хэндл созданной иконки или курсора.

К этому моменту мы научились добавлять изображения в список. А для удаления изображения нужно вызвать функцию ImageList_Remove(), описанную так:

WINCOMMCTRLAPI BOOL WINAPI ImageList_Removc(HIMAGELIST himl,

int i);

173

Автор уверен, что даже не заглядывая дальше, читатель догадался, что первым аргументом является хэндл списка изображений, а вторым -индекс удаляемого изображения. Если вместо индекса изображения подставить -1, то функция удалит все изображения из списка, но не удалит сам список. Для удаления всех изображений из списка можно воспользоваться макросом ImageList_RemoveAH(), единственным аргу­ментом которого является хэндл списка изображений.



Для замены изображения в списке служит функция ImageList_Replace(). Её описание находим в файле commctrl.h:

WINCOMMCTRLAPI BOOL WINAPI ImagcList_Replace(HIMAGFLLIST him],

int i,

HBITMAP hbmlmage, HBITMAP hbmMask);

Аргументы этой функции вполне понятны: первый - хэндл списка изображений; второй - индекс замещаемого изображения; третий и четвертый - хэндлы нового изображения и его маски. Если список немас­кированный, четвертый аргумент игнорируется.

Очередная функция - ImageList_ReplaceIcon() - описана так:

WINCOMMCTRLAPI int WINAPI ImageList_RcplaceIcon(HIMAGELIST himl,

int i. HICON hicon);

Нужно ли описывать аргументы этой функции?

Если в функции ImageEist_ReplaceIcon() второй аргумент заменить на -1, то иконка или курсор будут не замещать старое изображение, а добав­ляться в список. Этот нюанс используется в макросе ImageList_ AddlconQ, аргументами которого являются хэндл списка изображений и хэндл добавляемой иконки или курсора.

Вполне вероятно, что если второй аргумент функции ImageList_Replace() равен -1, то изображение не замещает старое, а добавляется. Проверку этой гипотезы я оставляю читателю.

Для того чтобы прорисовать изображение, хранящееся в списке, необ­ходимо использовать функцию ImageListJDraw(). Вполне понятно, что для прорисовки изображения вызывается что-то типа функции BitBlt(), которая требует два контекста устройства, координаты и прочее. Вспом­ним, что контекст устройства, в котором хранится список изображений, у нас ' есть. Координаты отдельного изображения моментально вычисляются по его индексу. И что остается? Хэндл контекста, на кото-

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

Функция ImageList_Draw() имеет следующий прототип:

WINCOMMCTRLAPI BOOL WINAPI ImageList_Draw(HIMAGELIST himl,

int i,

HOC hdcDst, intx, int y, UINT (Style);

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



Привлекательность использования списков изображений повышает еще одно обстоятельство. При использовании списков возможно исполь­зование специальных функций, позволяющих пользователю перемещать изображения (drag-and-drop) на экране: во-первых, с минимальными затратами на написание нового кода; во-вторых, без заметного мерцания.

Таблица 42. Флаги прорисовки отдельного изображения в списке изображений

Значение

ILD_NORMAL

ILD TRANSPARENT

ILD_BLEND25 ILD_BLEND50

ILD_MASK

ILD IMAGK

ILD OVERLAYMASK

ILD SELECTED

ILD FOCUS

ILD BLEND

0x0002 0x0004

0x0010

0x0020

OxOFOO

ILD BLEND50 ILD BLEND25 ILD~BLEND50

Обычное копирование изображения Каждый белый бит маски заставляет соответствующим бит изображения прорисовываться как прозрачный Снижение интенсивности цветов изобра­жения на 25 %

Снижение интенсивности цветов изобра­жения на 50 % Прорисовка маски Прорисовка изображения

Операция перемещения изображения начинается вызовом функции ImageList_BeginDrag(), прототип которой выглядит следующим образом:

WINCOMMCTRLAPI BOOL WINAPI ImageList_BcginDrag(IMAGELIST himlTrack,

int iTrack, int dxHotspot, int dyHotspot);

В число аргументов этой функции входят хэндл списка изображений, индекс перемещаемого изображения и координаты «горячего пятна» (hot spot'a - о проблемы перевода!) внутри изображения. Горячее пятно - это пиксель, по которому определяется точное положение изображения на экране. Обычно горячее пятно определяется таким образом, чтобы оно совпадало с горячим пятном курсора мыши. С этим горячим пятном нам еще предстоит помучиться.

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

Следом за функцией ImageList_BeginDrag() обычно используется функция ImageList_DragEnter(). Описание этой функции можно найти в файле commctrl.h:



WINCOMMCTRLAPI BOOL WINAPI ImagcList_DragEnter(IIWND hwndLock,

int x, int y);

Эта функция запрещает обновление указанного окна во время выпол­нения операции «drag-and-drop» и прорисовывает перемещаемое изобра­жение в промежуточных позициях (до того, как будет отпущена клавиша мыши). В некотором смысле можно сказать, что эта функция делает изображение курсором мыши (конечно, нельзя понимать это буквально, сходство чисто внешнее). Первый аргумент этой функции понятен -хэндл окна, обновление которого запрещается. Этим окном является то окно, в котором производится перемещение изображения. Второй и третий аргументы определяют координаты той точки, в которой необхо­димо прорисовать изображение. ВНИМАНИЕ! В данном случае необхо­димо указывать координаты оконные, а не координаты в рабочей области окна. Таким образом, до обращения к этой функции необходимо опреде­лить ширину границы окна, высоту заголовка и, при необходимости, ширину полосы меню. В демонстрационной программе это сделано при

функции GetSystemMetrics(). Рекомендую читателю изучить эту функцию самостоятельно.

Функция ImageListJDragMove() описана следующим образом:

WINCOMMCTRLAPI BOOL WINAPI I mage I ist_DragMove(int x, int y);

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

Последней функцией, обеспечивающей «drag-and-drop», является lmageList_Endnrag(). Эта функция завершает перемещение, но не разре­шает обновление окна и не производит прорисовку перемещенного изображения. Для разрешения обновления окна необходимо вызвать функцию ImageList_DragLeave(), передав ей в качестве аргумента хэндл окна, а затем прорисовать изображение, например, с помощью функции ImageList Draw().

Л теперь, как всегда, демонстрационная программа. В этой программе при создании окна производится прорисовка двух икон в левой верхней части рабочей области. Иконки могут быть скопированы в другое место при использовании операции «drag-and-drop». Думаю, читателю не соста­вит труда при необходимости изменить эту программу так, чтобы иконки не копировались, а перемещались.



При разработке программы я допустил определенного рода плагиат. Одну из иконок я «выдрал» из программы pview95, другую - из примера mixtree, поставляемых с Borland Си- 5.0. Если читатель захочет, то он легко может заменить иконки на свои.

//include <windo\vs.h> //include <commctrl.h>

//define CX_ICON 32 //define CYJCON 32

UINSTANCE hlnst;

LRLSULT CALLBACK ImageListWndProc ( HWND, UINT, UINT, LONG );

int WINAPI WinMain (HINSTANCE hlnstance, HINSTANCE hl'revlnstancc, LPSTR IpszCmdParam, int nCmdShow )

HWND hWnd ;

WNDCLASS WndClass ;

MSG Msg;

char szClassNamcf] ^ "ImageList";

177

hlnst = hlnstance; /* Registering our window class */ /* Fill WNDCLASS structure */

WndClass.style = CS_HREDRAW | CS_VREDRAW;

WndClass.lpfnWndProc = ImageListWndProc;

WndClass.cbClsExtra = 0;

WndCIass.cbWndExtra = 0;

WndClass.hlnstance = hlnstance ;

WndClass.hlcon = Loadlcon (NULL,IDI_APPLICATION);

WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);

WndClass.hbrBackground = (HBRUSH) GetStockObjcct (WHITE_BRUSH);

WndClass.lpszMenuName = "";

WndClass.lpszClassName = szClassName;

if ( !RegisterClass(&WndClass)) {

MessageBox(NULL,"Cannot register class","Error",MBJ3K); return 0;

}

hWnd = CreateWindow(szClassName, "Image List Demo Program",

WS_OVERLAPPEDWINDOW, CWJJSEDEFAULT, CWJJSEDEFAULT, CW_USEDEFAULT, CWJJSEDEFAULT, NULL, NULL, hlnstance.NULL);

if(!hWnd)

{

MessageBox(NULL,"Cannot create window», «Error», MB_OK); return 0; }

InitCommonControlsO; /* Show our window */ ShowWindow(hWnd,nCmdSliow); UpdateWindow(hWnd);

/* Beginning of messages cycle */

whi!e(GetMessage(&Msg, NULL, 0, 0))

t i

TranslateMessage(&Msg); DispatchMessage(&Msg);

} return Msg.wParam;

LRESULT CALLBACK ImageListWndProc (HWND hWnd, UINT Message,

UINT wParam, LONG IParam )

<

static HIMAGELIST hlmageList;

static int i;

static HOC hDC, hPaintDC;

PAINTSTRUCT PaintStruct;

RECT rBigRect = {0, 0, CXJCON * 2, CYJCON};



RECT rLittleRecll = {0, 0. CX_ICON, CYJCON};

static POINT Point, pHotSpot;

static BOOL bCapture = FALSE;

static int nXBordcr, nYBordcr. nYCaption;

switch(Message) {

case WM_CREATE: hDC = GctDC(hWnd);

hlmageList - IniageListJJreate(CXJCON, CYJCON, ILCJvIASK, 3, 3); ImageList_AddIcon(hImageList, Loadlmage(hlnst, "Mixtree.ico",

IMAGEJCON, 0, 0, LRJJ3ADFROMFILE)); ImageList_AddIcon(hImageList, Loadlmage(hlnst, "Pview95.ico",

IMAGEJCON, 0, 0, LRJ.OADFROMFILE)); nXBordcr = GetSystemMclrics(SM_CXBORDER); nYBorder = GetSystemMctrics(SMJCYBORDER); nYCaption = GetSystemMetrics(SMJCYCAPTION); return 0;

case WMJ'AINT:

hPaintDC = BcginPaint(hWnd, &PaintStruc(); for(i = 0; i < 2; i++) Image-ListJ)raw(hImageList, i, hPaintDC, i * CXJCON, 0,

ILD_NORMAL); EndPaint(hWnd. &PaintStruct); return 0;

case WMJ.BUTTONDOWN: Point.x = LOWORD(lParam); Point.y = HIWORD(lParam); if(PtInRect(&rBigRect, Point)) >

SetCapture(hWnd); bCapture = TRUE; if(Pt!nRect(&rLittlcRectI, Point))

i = 0; else

i= I;

pHotSpot.x = Point.x - i * CXJCON; pHotSpot.y = Point.y;

ImageListJ3eginDrag(hImageList, i, pHotSpot.x, pHotSpot.y); ImageListJJragEnterfhWnd, Point.x + nXBordcr, Point.y + nYBorder +

nYCaption); }

return 0;

case WMJVIOUSEMOVE: iffbCapture) ImageListJ>agMove(LOWORD(!Param), HIWORD(lParam) + nYBorder

-i- nYCaption); return 0;

case WM_LBUTTONUP: if(bCapture)

ImageListJSndDragO;

ImageList_DragLeave(hWnd);

ImageListJ>aw(hImageList, i, hDC, LOWORD(lParam)- pHotSpot.x,

HIWORD(lParam) - pHolSpot.y, ILDJMORMAL); ReleaseCapturc(); bCapture = FALSE;

}

return 0;

case WM_DESTROY: ReleascDC(hWnd, hDC); ImageList_Destroy(hImageList); PostQuitMessage(O); return 0;

\ j

return DefWindowProc(hWnd,Message,wParam, IParam); >

Вид окна до произведения операций «drag-and-drop» показан на рис. 16. На рис. 17 приведен вид этого же окна после выполнения нескольких операций копирования иконок.

Мне бы хотелось, чтобы читатель обратил внимание на возможность лег­кого перемещения и копирования изображений. Что бы пришлось делать в том случае, если бы здесь не использовался список изображений?



В Image List Demo Progiam



Рис. 16. Окно с двумя изображениями in списка изображений до операции "drag-and-drop"

Ш Image List Demo Program

mmm\



Рис. 17. Предыдущее окно после нескольких операций "drag-and-drop"