WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     | 1 |   ...   | 8 | 9 || 11 | 12 |

«Ч. Петзолд Программирование для Windows® 95 в двух томах Том I «BHV — Санкт-Петербург» Дюссельдорф Киев Москва Санкт-Петербург Содержание ЧАСТЬ I ВВЕДЕНИЕ ...»

-- [ Страница 10 ] --

Возможно, вершиной программистской лени является программа HEXCALC, приведенная на рис. 11.9. В этой программе функция CreateWindow вообще не вызывается, нигде не обрабатываются сообщения WM_PAINT, нигде не получают контекста устройства и нигде не обрабатываются сообщения мыши. Несмотря на это, программа работает, представляя собой менее 150 строк исходного текста, и являясь шестнадцатеричным калькулятором, реализующим 10 функций, и имеющим законченный интерфейс клавиатуры и мыши. Окно программы калькулятора показано на рис. 11.10.

HEXCALC.MAK #----------------------- # HEXCALC.MAK make file #----------------------- hexcalc.exe : hexcalc.obj hexcalc.res $(LINKER) $(GUIFLAGS) -OUT:hexcalc.exe hexcalc.obj hexcalc.res $(GUILIBS) hexcalc.obj : hexcalc.c $(CC) $(CFLAGS) hexcalc.c hexcalc.res : hexcalc.rc hexcalc.ico $(RC) $(RCVARS) hexcalc.rc HEXCALC.C /*---------------------------------------- HEXCALC.C -- Hexadecimal Calculator (c) Charles Petzold, ----------------------------------------*/ #include #include #include #include #include LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static char szAppName[] = "HexCalc";

HWND hwnd;

MSG msg;

WNDCLASSEX wndclass;

wndclass.cbSize = sizeof(wndclass);

wndclass.style = CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc = WndProc;

wndclass.cbClsExtra = 0;

wndclass.cbWndExtra = DLGWINDOWEXTRA;

wndclass.hInstance = hInstance;

wndclass.hIcon = LoadIcon(hInstance, szAppName);

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

wndclass.hbrBackground =(HBRUSH)(COLOR_WINDOW + 1);

wndclass.lpszMenuName = NULL;

wndclass.lpszClassName = szAppName;

wndclass.hIconSm = LoadIcon(hInstance, szAppName);

RegisterClassEx(&wndclass);

hwnd = CreateDialog(hInstance, szAppName, 0, NULL);

ShowWindow(hwnd, iCmdShow);

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

DispatchMessage(&msg);

} return msg.wParam;

} void ShowNumber(HWND hwnd, UINT iNumber) { char szBuffer[20];

SetDlgItemText(hwnd, VK_ESCAPE, strupr(ltoa(iNumber, szBuffer, 16)));

} DWORD CalcIt(UINT iFirstNum, int iOperation, UINT iNum) { switch(iOperation) { case '=' : return iNum;

case '+' : return iFirstNum + iNum;

case '-' : return iFirstNum - iNum;

case '*' : return iFirstNum * iNum;

case '&' : return iFirstNum & iNum;

case '|' : return iFirstNum | iNum;

case '^' : return iFirstNum ^ iNum;

case '<' : return iFirstNum << iNum;

case '>' : return iFirstNum >> iNum;

case '/' : return iNum ? iFirstNum / iNum : UINT_MAX;

case '%' : return iNum ? iFirstNum % iNum : UINT_MAX;

default : return 0;

} } LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static BOOL bNewNumber = TRUE;

static int iOperation = '=';

static UINT iNumber, iFirstNum;

HWND hButton;

switch(iMsg) { case WM_KEYDOWN : // left arrow --> backspace if(wParam != VK_LEFT) break;

wParam = VK_BACK;

// fall through case WM_CHAR :

if((wParam = toupper(wParam)) == VK_RETURN) wParam = '=';

hButton = GetDlgItem(hwnd, wParam);

if(hButton != NULL) { SendMessage(hButton, BM_SETSTATE, 1, 0);

SendMessage(hButton, BM_SETSTATE, 0, 0);

} else { MessageBeep(0);

break;

} // fall through case WM_COMMAND :

SetFocus(hwnd);

if(LOWORD(wParam) == VK_BACK) // backspace ShowNumber(hwnd, iNumber /= 16);

else if(LOWORD(wParam) == VK_ESCAPE) // escape ShowNumber(hwnd, iNumber = 0);

else if(isxdigit(LOWORD(wParam))) // hex digit { if(bNewNumber) { iFirstNum = iNumber;

iNumber = 0;

} bNewNumber = FALSE;

if(iNumber <= UINT_MAX >> 4) ShowNumber(hwnd, iNumber = 16 * iNumber + wParam - (isdigit(wParam) ? '0' : 'A' - 10));

else MessageBeep(0);

} else // operation { if(!bNewNumber) ShowNumber(hwnd, iNumber = CalcIt(iFirstNum, iOperation, iNumber));

bNewNumber = TRUE;

iOperation = LOWORD(wParam);

} return 0;

case WM_DESTROY :

PostQuitMessage(0);

return 0;

} return DefWindowProc(hwnd, iMsg, wParam, lParam);

} HEXCALC.RC /*---------------------------- HEXCALC.RC resource script ----------------------------*/ #include HexCalc ICON hexcalc.ico HexCalc DIALOG 32768, 0, 102, STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX CLASS "HexCalc" CAPTION "Hex Calculator" { PUSHBUTTON "D", 68, 8, 24, 14, PUSHBUTTON "A", 65, 8, 40, 14, PUSHBUTTON "7", 55, 8, 56, 14, PUSHBUTTON "4", 52, 8, 72, 14, PUSHBUTTON "1", 49, 8, 88, 14, PUSHBUTTON "0", 48, 8, 104, 14, PUSHBUTTON "0", 27, 26, 4, 50, PUSHBUTTON "E", 69, 26, 24, 14, PUSHBUTTON "B", 66, 26, 40, 14, PUSHBUTTON "8", 56, 26, 56, 14, PUSHBUTTON "5", 53, 26, 72, 14, PUSHBUTTON "2", 50, 26, 88, 14, PUSHBUTTON "Back", 8, 26, 104, 32, PUSHBUTTON "C", 67, 44, 40, 14, PUSHBUTTON "F", 70, 44, 24, 14, PUSHBUTTON "9", 57, 44, 56, 14, PUSHBUTTON "6", 54, 44, 72, 14, PUSHBUTTON "3", 51, 44, 88, 14, PUSHBUTTON "+", 43, 62, 24, 14, PUSHBUTTON "-", 45, 62, 40, 14, PUSHBUTTON "*", 42, 62, 56, 14, PUSHBUTTON "/", 47, 62, 72, 14, PUSHBUTTON "%", 37, 62, 88, 14, PUSHBUTTON "Equals", 61, 62, 104, 32, PUSHBUTTON "&&", 38, 80, 24, 14, PUSHBUTTON "|", 124, 80, 40, 14, PUSHBUTTON "^", 94, 80, 56, 14, PUSHBUTTON "<", 60, 80, 72, 14, PUSHBUTTON ">", 62, 80, 88, 14, } HEXCALC.ICO Рис. 11.9 Программа HEXCALC Рис. 11.10 Окно программы HEXCALC Программа HEXCALC — это обычный калькулятор с нефиксированной записью, использующий обозначение языка C для записи операций. Он работает с беззнаковыми 32-разрядными целыми и может выполнять сложение, вычитание, умножение, деление и определять остаток от деления;

осуществлять поразрядные операции AND, OR и исключающее OR;

сдвигать числа влево или вправо на один разряд. Деление на 0 приводит к результату FFFFFFFF.

Для работы с калькулятором в программе HEXCALC можно использовать как мышь, так и клавиатуру. Начинать надо с ввода первого числа, щелкнув мышью над ним или набрав его на клавиатуре (до восьми шестнадцатеричных цифр), затем вводится знак операции и второе число. Вывести результат можно либо щелкнув мышью на кнопке Equals, либо нажав клавиши или . Для исправления ввода используется кнопка Back, либо клавиши или "Стрелка влево". Щелчок в окошке с результатом или нажатие клавиши полностью стирает текущий результат.

Необычность программы HEXCALC состоит в том, что выводимое на экран окно кажется гибридом обычного перекрывающегося окна и немодального окна диалога. С одной стороны, все сообщения в программе HEXCALC обрабатываются в функции WndProc, которая, как кажется, является обычной оконной процедурой. Функция возвращает длинное целое число и обрабатывает сообщение WM_DESTROY и, как обычная оконная процедура, вызывает DefWindowProc. С другой стороны, окно создается в функции WinMain с помощью вызова функции CreateDialog, использующей шаблон окна диалога из файла HEXCALC.RC. Итак, что же представляет из себя программа HEXCALC, обычное перекрывающееся окно или немодальное окно диалога?

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

Внимательное изучение файла HEXCALC.RC позволит обнаружить, как это делается. Начало шаблона окна диалога выглядит так:

HexCalc DIALOG 32768, 0, 102, STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX CLASS "HexCalc" CAPTION "Hex Calculator" Обратите внимание на использование идентификаторов, таких как WS_OVERLAPPED и WS_MINIMIZEBOX, которые можно было бы использовать и для создания обычного окна с помощью функции CreateWindow.

Решающее отличие между нашим окном диалога и всем тем, что мы создавали до сих пор, заключено в инструкции CLASS. Когда в предыдущих шаблонах окон диалога эта инструкция не использовалась, Windows регистрировала класс окна диалога и использовала собственную оконную процедуру для обработки сообщений окна диалога. Включение в шаблон диалога инструкции CLASS сообщает Windows о необходимости отправлять сообщения куда-то в другое место, а точнее в оконную процедуру, заданную в классе окна "HexCalc".

Класс окна "HexCalc" регистрируется в функции WinMain программы HEXCALC точно также, как регистрируется класс обычного окна. Однако, обратите внимание на следующее очень важное отличие: поле cbWndExtra структуры WNDCLASS устанавливается в значение DLGWINDOWEXTRA. Это существенно для тех процедур диалога, которые регистрируются в приложении.

После регистрации класса окна, WinMain вызывает функцию CreateDialog:

hwnd = CreateDialog(hInstance, szAppName, 0, NULL);

Второй параметр (строка "HexCalc") является именем шаблона окна диалога. Третий параметр, который обычно является описателем родительского окна, устанавливается в 0, поскольку в нашем случае родительское окно отсутствует. Последний параметр, который обячно является адресом процедуры диалога, в нашем случае не требуется, поскольку Windows не обрабатывает сообщений и, следовательно, не может отправить их в процедуру диалога.

Вызов такой функции CreateDialog вместе с шаблоном окна диалога преобразуются операционной системой Windows именно в вызов функции CreateWindow, которая выглядит следующим образом:

hwnd = CreateWindow("HexCalc", "Hex Calculator", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 102 * 4 / cxChar, 122 * 8 / cyChar, NULL, NULL, hInstance, NULL);

Переменные cxChar и cyChar являются высотой и шириной символа системного шрифта.

Мы извлекли колоссальную выгоду, позволив Windows создать такой вызов функции CreateWindow: Windows не ограничится созданием одного всплывающего окна, она также будет вызывать функцию CreateWindow для создания всех 29 дочерних окон управления, которыми являются кнопки, определенные в шаблоне окна диалога.

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

Творческое использование идентификаторов дочерних окон элементов управления В программе HEXCALC нет заголовочного файла с идентификаторами всех дочерних окон управления, определенных в шаблоне окна диалога. Обойтись без этого файла можно, поскольку идентификационным номером каждой из кнопок установлен код ASCII того текста, который появляется на кнопке при ее выводе на экран. Это означает, что WndProc может обращаться с сообщениями WM_COMMAND и WM_CHAR почти одинаково. В каждом случае параметр wParam является кодом ASCII текста кнопки.

Конечно, небольшая модификация сообщений от клавиатуры все же необходима. WndProc обрабатывает сообщения WM_KEYDOWN таким образом, чтобы преобразовать клавишу "стрелка влево" в клавишу . При обработке сообщений WM_CHAR WndProc преобразует код символов к верхнему регистру, а клавишу к ASCII-коду клавиши .

Правильность сообщений WM_CHAR контролируется с помощью вызова функции GetDlgItem. Если возвращаемым значением функции является 0, то значит символ клавиатуры не является одним из идентификаторов, определенных в шаблоне окна диалога. Однако, если символ один из этих идентификаторов, то соответствующей кнопке посылается пара сообщений BM_SETSTATE, так, что она в момент нажатия "залипает":

hButton =(GetDlgItem(hwnd, wParam);

if(hButton != NULL) { SendMessage(hButton, BM_SETSTATE, 1, 0);

SendMessage(hButton, BM_SETSTATE, 0, 0);

} Это с минимальными усилиями делает более привлекательным процесс нажатия клавиш в интерфейсе программы HEXCALC.

Когда WndProc обрабатывает сообщения WM_COMMAND, она всегда помещает фокус ввода в родительское окно:

case WM_COMMAND:

SetFocus(hwnd);

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

Диалоговые окна общего пользования Одной из первостепенных задач Windows было содействовать созданию стандартизированного интерфейса пользователя. Для многих общепринятых пунктов меню это было сделано достаточно быстро. Почти все производители программного обеспечения для открытия файла использовали последовательность Alt-File-Open.

Однако, сами окна диалога для открытия файла часто были совершенно непохожи.

Начиная с версии Windows 3.1, решение этой задачи было найдено и продолжает обеспечиваться под Windows 95.

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

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

Функции и структуры, которые необходимы для использования этих окон диалога, определяются в заголовочном файле COMMDLG.H. Файл COMDLG32.LIB представляет собой библиотеку импорта диалоговых окон общего пользования, а файл COMDLG32.DLL — это динамически подключаемая библиотека, в которой содержатся шаблоны и процедуры окон диалога.

Модернизированная программа POPPAD Когда в главе 10 к программе POPPAD добавили меню, несколько стандартных опций меню не были реализованы.

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

Файлы, в которых находится программа POPPAD3, представлены на рис. 11.11.

POPPAD3.MAK #----------------------- # POPPAD3.MAK make file #----------------------- poppad3.exe : poppad.obj popfile.obj popfind.obj \ popfont.obj popprnt0.obj poppad.res $(LINKER) $(GUIFLAGS) -OUT:poppad3.exe poppad.obj popfile.obj \ popfind.obj popfont.obj popprnt0.obj poppad.res $(GUILIBS) poppad.obj : poppad.c poppad.h $(CC) $(CFLAGS) poppad.c popfile.obj : popfile.c $(CC) $(CFLAGS) popfile.c popfind.obj : popfind.c $(CC) $(CFLAGS) popfind.c popfont.obj : popfont.c $(CC) $(CFLAGS) popfont.c popprnt0.obj : popprnt0.c $(CC) $(CFLAGS) popprnt0.c poppad.res : poppad.rc poppad.h poppad.ico $(RC) $(RCVARS) poppad.rc POPPAD.C /*--------------------------------------- POPPAD.C -- Popup Editor (c) Charles Petzold, ---------------------------------------*/ #include #include #include #include "poppad.h" #define EDITID #define UNTITLED "(untitled)" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

BOOL CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);

// Functions in POPFILE.C void PopFileInitialize(HWND);

BOOL PopFileOpenDlg (HWND, PSTR, PSTR);

BOOL PopFileSaveDlg (HWND, PSTR, PSTR);

BOOL PopFileRead (HWND, PSTR);

BOOL PopFileWrite (HWND, PSTR);

// Functions in POPFIND.C HWND PopFindFindDlg (HWND);

HWND PopFindReplaceDlg (HWND);

BOOL PopFindFindText (HWND, int *, LPFINDREPLACE);

BOOL PopFindReplaceText(HWND, int *, LPFINDREPLACE);

BOOL PopFindNextText (HWND, int *);

BOOL PopFindValidFind (void);

// Functions in POPFONT.C void PopFontInitialize (HWND);

BOOL PopFontChooseFont (HWND);

void PopFontSetFont (HWND);

void PopFontDeinitialize(void);

// Functions in POPPRNT.C BOOL PopPrntPrintFile(HINSTANCE, HWND, HWND, PSTR);

// Global variables static char szAppName[] = "PopPad";

static HWND hDlgModeless;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg;

HWND hwnd;

HACCEL hAccel;

WNDCLASSEX wndclass;

wndclass.cbSize = sizeof(wndclass);

wndclass.style = CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc = WndProc;

wndclass.cbClsExtra = 0;

wndclass.cbWndExtra = 0;

wndclass.hInstance = hInstance;

wndclass.hIcon = LoadIcon(hInstance, szAppName);

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

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

wndclass.lpszMenuName = szAppName;

wndclass.lpszClassName = szAppName;

wndclass.hIconSm = LoadIcon(hInstance, szAppName);

RegisterClassEx(&wndclass);

hwnd = CreateWindow(szAppName, NULL, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, szCmdLine);

ShowWindow(hwnd, iCmdShow);

UpdateWindow(hwnd);

hAccel = LoadAccelerators(hInstance, szAppName);

while(GetMessage(&msg, NULL, 0, 0)) { if(hDlgModeless == NULL || !IsDialogMessage(hDlgModeless, &msg)) { if(!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg);

DispatchMessage(&msg);

} } } return msg.wParam;

} void DoCaption(HWND hwnd, char *szTitleName) { char szCaption[64 + _MAX_FNAME + _MAX_EXT];

wsprintf(szCaption, "%s - %s", szAppName, szTitleName[0] ? szTitleName : UNTITLED);

SetWindowText(hwnd, szCaption);

} void OkMessage(HWND hwnd, char *szMessage, char *szTitleName) { char szBuffer[64 + _MAX_FNAME + _MAX_EXT];

wsprintf(szBuffer, szMessage, szTitleName[0] ? szTitleName : UNTITLED);

MessageBox(hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION);

} short AskAboutSave(HWND hwnd, char *szTitleName) { char szBuffer[64 + _MAX_FNAME + _MAX_EXT];

int iReturn;

wsprintf(szBuffer, "Save current changes in %s?", szTitleName[0] ? szTitleName : UNTITLED);

iReturn = MessageBox(hwnd, szBuffer, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION);

if(iReturn == IDYES) if(!SendMessage(hwnd, WM_COMMAND, IDM_SAVE, 0L)) iReturn = IDCANCEL;

return iReturn;

} LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static BOOL bNeedSave = FALSE;

static char szFileName[_MAX_PATH];

static char szTitleName[_MAX_FNAME + _MAX_EXT];

static HINSTANCE hInst;

static HWND hwndEdit;

static int iOffset;

static UINT iMsgFindReplace;

int iSelBeg, iSelEnd, iEnable;

LPFINDREPLACE pfr;

switch(iMsg) { case WM_CREATE :

hInst =((LPCREATESTRUCT) lParam) -> hInstance;

// Create the edit control child window hwndEdit = CreateWindow("edit", NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd,(HMENU) EDITID, hInst, NULL);

SendMessage(hwndEdit, EM_LIMITTEXT, 32000, 0L);

// Initialize common dialog box stuff PopFileInitialize(hwnd);

PopFontInitialize(hwndEdit);

iMsgFindReplace = RegisterWindowMessage(FINDMSGSTRING);

// Process command line lstrcpy(szFileName,(PSTR) (((LPCREATESTRUCT) lParam)->lpCreateParams));

if(strlen(szFileName) > 0) { GetFileTitle(szFileName, szTitleName, sizeof(szTitleName));

if(!PopFileRead(hwndEdit, szFileName)) OkMessage(hwnd, "File %s cannot be read!", szTitleName);

} DoCaption(hwnd, szTitleName);

return 0;

case WM_SETFOCUS :

SetFocus(hwndEdit);

return 0;

case WM_SIZE :

MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);

return 0;

case WM_INITMENUPOPUP :

switch(lParam) { case 1 : // Edit menu // Enable Undo if edit control can do it EnableMenuItem((HMENU) wParam, IDM_UNDO, SendMessage(hwndEdit, EM_CANUNDO, 0, 0L) ?

MF_ENABLED : MF_GRAYED);

// Enable Paste if text is in the clipboard EnableMenuItem((HMENU) wParam, IDM_PASTE, IsClipboardFormatAvailable(CF_TEXT) ?

MF_ENABLED : MF_GRAYED);

// Enable Cut, Copy, and Del if text is selected SendMessage(hwndEdit, EM_GETSEL,(WPARAM) &iSelBeg, (LPARAM) &iSelEnd);

iEnable = iSelBeg != iSelEnd ? MF_ENABLED : MF_GRAYED;

EnableMenuItem((HMENU) wParam, IDM_CUT, iEnable);

EnableMenuItem((HMENU) wParam, IDM_COPY, iEnable);

EnableMenuItem((HMENU) wParam, IDM_CLEAR, iEnable);

break;

case 2 : // Search menu // Enable Find, Next, and Replace if modeless // dialogs are not already active iEnable = hDlgModeless == NULL ?

MF_ENABLED : MF_GRAYED;

EnableMenuItem((HMENU) wParam, IDM_FIND, iEnable);

EnableMenuItem((HMENU) wParam, IDM_NEXT, iEnable);

EnableMenuItem((HMENU) wParam, IDM_REPLACE, iEnable);

break;

} return 0;

case WM_COMMAND :

// Messages from edit control if(lParam && LOWORD(wParam) == EDITID) { switch(HIWORD(wParam)) { case EN_UPDATE :

bNeedSave = TRUE;

return 0;

case EN_ERRSPACE :

case EN_MAXTEXT :

MessageBox(hwnd, "Edit control out of space.", szAppName, MB_OK | MB_ICONSTOP);

return 0;

} break;

} switch(LOWORD(wParam)) { // Messages from File menu case IDM_NEW :

if(bNeedSave && IDCANCEL == AskAboutSave(hwnd, szTitleName)) return 0;

SetWindowText(hwndEdit, "\0");

szFileName[0] = '\0';

szTitleName[0] = '\0';

DoCaption(hwnd, szTitleName);

bNeedSave = FALSE;

return 0;

case IDM_OPEN :

if(bNeedSave && IDCANCEL == AskAboutSave(hwnd, szTitleName)) return 0;

if(PopFileOpenDlg(hwnd, szFileName, szTitleName)) { if(!PopFileRead(hwndEdit, szFileName)) { OkMessage(hwnd, "Could not read file %s!", szTitleName);

szFileName[0] = '\0';

szTitleName[0] = '\0';

} } DoCaption(hwnd, szTitleName);

bNeedSave = FALSE;

return 0;

case IDM_SAVE :

if(szFileName[0]) { if(PopFileWrite(hwndEdit, szFileName)) { bNeedSave = FALSE;

return 1;

} else OkMessage(hwnd, "Could not write file %s", szTitleName);

return 0;

} // fall through case IDM_SAVEAS :

if(PopFileSaveDlg(hwnd, szFileName, szTitleName)) { DoCaption(hwnd, szTitleName);

if(PopFileWrite(hwndEdit, szFileName)) { bNeedSave = FALSE;

return 1;

} else OkMessage(hwnd, "Could not write file %s", szTitleName);

} return 0;

case IDM_PRINT :

if(!PopPrntPrintFile(hInst, hwnd, hwndEdit, szTitleName)) OkMessage(hwnd, "Could not print file %s", szTitleName);

return 0;

case IDM_EXIT :

SendMessage(hwnd, WM_CLOSE, 0, 0);

return 0;

// Messages from Edit menu case IDM_UNDO :

SendMessage(hwndEdit, WM_UNDO, 0, 0);

return 0;

case IDM_CUT :

SendMessage(hwndEdit, WM_CUT, 0, 0);

return 0;

case IDM_COPY :

SendMessage(hwndEdit, WM_COPY, 0, 0);

return 0;

case IDM_PASTE :

SendMessage(hwndEdit, WM_PASTE, 0, 0);

return 0;

case IDM_CLEAR :

SendMessage(hwndEdit, WM_CLEAR, 0, 0);

return 0;

case IDM_SELALL :

SendMessage(hwndEdit, EM_SETSEL, 0, -1);

return 0;

// Messages from Search menu case IDM_FIND :

SendMessage(hwndEdit, EM_GETSEL, NULL, (LPARAM) &iOffset);

hDlgModeless = PopFindFindDlg(hwnd);

return 0;

case IDM_NEXT :

SendMessage(hwndEdit, EM_GETSEL, NULL, (LPARAM) &iOffset);

if(PopFindValidFind()) PopFindNextText(hwndEdit, &iOffset);

else hDlgModeless = PopFindFindDlg(hwnd);

return 0;

case IDM_REPLACE :

SendMessage(hwndEdit, EM_GETSEL, NULL, (LPARAM) &iOffset);

hDlgModeless = PopFindReplaceDlg(hwnd);

return 0;

case IDM_FONT :

if(PopFontChooseFont(hwnd)) PopFontSetFont(hwndEdit);

return 0;

// Messages from Help menu case IDM_HELP :

OkMessage(hwnd, "Help not yet implemented!", "\0");

return 0;

case IDM_ABOUT :

DialogBox(hInst, "AboutBox", hwnd, AboutDlgProc);

return 0;

} break;

case WM_CLOSE :

if(!bNeedSave || IDCANCEL != AskAboutSave(hwnd, szTitleName)) DestroyWindow(hwnd);

return 0;

case WM_QUERYENDSESSION :

if(!bNeedSave || IDCANCEL != AskAboutSave(hwnd, szTitleName)) return 1;

return 0;

case WM_DESTROY :

PopFontDeinitialize();

PostQuitMessage(0);

return 0;

default:

// Process "Find-Replace" iMsgs if(iMsg == iMsgFindReplace) { pfr =(LPFINDREPLACE) lParam;

if(pfr->Flags & FR_DIALOGTERM) hDlgModeless = NULL;

if(pfr->Flags & FR_FINDNEXT) if(!PopFindFindText(hwndEdit, &iOffset, pfr)) OkMessage(hwnd, "Text not found!", "\0");

if(pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL) if(!PopFindReplaceText(hwndEdit, &iOffset, pfr)) OkMessage(hwnd, "Text not found!", "\0");

if(pfr->Flags & FR_REPLACEALL) while(PopFindReplaceText(hwndEdit, &iOffset, pfr));

return 0;

} break;

} return DefWindowProc(hwnd, iMsg, wParam, lParam);

} BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) { switch(iMsg) { case WM_INITDIALOG :

return TRUE;

case WM_COMMAND :

switch(LOWORD(wParam)) { case IDOK :

EndDialog(hDlg, 0);

return TRUE;

} break;

} return FALSE;

} POPFILE.C /*------------------------------------------ POPFILE.C -- Popup Editor File Functions ------------------------------------------*/ #include #include #include #include static OPENFILENAME ofn;

void PopFileInitialize(HWND hwnd) { static char szFilter[] = "Text Files(*.TXT)\0*.txt\0" \ "ASCII Files(*.ASC)\0*.asc\0" \ "All Files(*.*)\0*.*\0\0";

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.hwndOwner = hwnd;

ofn.hInstance = NULL;

ofn.lpstrFilter = szFilter;

ofn.lpstrCustomFilter = NULL;

ofn.nMaxCustFilter = 0;

ofn.nFilterIndex = 0;

ofn.lpstrFile = NULL;

// Set in Open and Close functions ofn.nMaxFile = _MAX_PATH;

ofn.lpstrFileTitle = NULL;

// Set in Open and Close functions ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT;

ofn.lpstrInitialDir = NULL;

ofn.lpstrTitle = NULL;

ofn.Flags = 0;

// Set in Open and Close functions ofn.nFileOffset = 0;

ofn.nFileExtension = 0;

ofn.lpstrDefExt = "txt";

ofn.lCustData = 0L;

ofn.lpfnHook = NULL;

ofn.lpTemplateName = NULL;

} BOOL PopFileOpenDlg(HWND hwnd, PSTR pstrFileName, PSTR pstrTitleName) { ofn.hwndOwner = hwnd;

ofn.lpstrFile = pstrFileName;

ofn.lpstrFileTitle = pstrTitleName;

ofn.Flags = OFN_HIDEREADONLY | OFN_CREATEPROMPT;

return GetOpenFileName(&ofn);

} BOOL PopFileSaveDlg(HWND hwnd, PSTR pstrFileName, PSTR pstrTitleName) { ofn.hwndOwner = hwnd;

ofn.lpstrFile = pstrFileName;

ofn.lpstrFileTitle = pstrTitleName;

ofn.Flags = OFN_OVERWRITEPROMPT;

return GetSaveFileName(&ofn);

} static long PopFileLength(FILE *file) { int iCurrentPos, iFileLength;

iCurrentPos = ftell(file);

fseek(file, 0, SEEK_END);

iFileLength = ftell(file);

fseek(file, iCurrentPos, SEEK_SET);

return iFileLength;

} BOOL PopFileRead(HWND hwndEdit, PSTR pstrFileName) { FILE *file;

int iLength;

PSTR pstrBuffer;

if(NULL ==(file = fopen(pstrFileName, "rb"))) return FALSE;

iLength = PopFileLength(file);

if(NULL ==(pstrBuffer =(PSTR) malloc(iLength))) { fclose(file);

return FALSE;

} fread(pstrBuffer, 1, iLength, file);

fclose(file);

pstrBuffer[iLength] = '\0';

SetWindowText(hwndEdit, pstrBuffer);

free(pstrBuffer);

return TRUE;

} BOOL PopFileWrite(HWND hwndEdit, PSTR pstrFileName) { FILE *file;

int iLength;

PSTR pstrBuffer;

if(NULL ==(file = fopen(pstrFileName, "wb"))) return FALSE;

iLength = GetWindowTextLength(hwndEdit);

if(NULL ==(pstrBuffer =(PSTR) malloc(iLength + 1))) { fclose(file);

return FALSE;

} GetWindowText(hwndEdit, pstrBuffer, iLength + 1);

if(iLength !=(int) fwrite(pstrBuffer, 1, iLength, file)) { fclose(file);

free(pstrBuffer);

return FALSE;

} fclose(file);

free(pstrBuffer);

return TRUE;

} POPFIND.C /*-------------------------------------------------------- POPFIND.C -- Popup Editor Search and Replace Functions --------------------------------------------------------*/ #include #include #include #define MAX_STRING_LEN static char szFindText [MAX_STRING_LEN];

static char szReplText [MAX_STRING_LEN];

HWND PopFindFindDlg(HWND hwnd) { static FINDREPLACE fr;

// must be static for modeless dialog!!!

fr.lStructSize = sizeof(FINDREPLACE);

fr.hwndOwner = hwnd;

fr.hInstance = NULL;

fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD;

fr.lpstrFindWhat = szFindText;

fr.lpstrReplaceWith = NULL;

fr.wFindWhatLen = sizeof(szFindText);

fr.wReplaceWithLen = 0;

fr.lCustData = 0;

fr.lpfnHook = NULL;

fr.lpTemplateName = NULL;

return FindText(&fr);

} HWND PopFindReplaceDlg(HWND hwnd) { static FINDREPLACE fr;

// must be static for modeless dialog!!!

fr.lStructSize = sizeof(FINDREPLACE);

fr.hwndOwner = hwnd;

fr.hInstance = NULL;

fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD;

fr.lpstrFindWhat = szFindText;

fr.lpstrReplaceWith = szReplText;

fr.wFindWhatLen = sizeof(szFindText);

fr.wReplaceWithLen = sizeof(szReplText);

fr.lCustData = 0;

fr.lpfnHook = NULL;

fr.lpTemplateName = NULL;

return ReplaceText(&fr);

} BOOL PopFindFindText(HWND hwndEdit, int *piSearchOffset, LPFINDREPLACE pfr) { int iLength, iPos;

PSTR pstrDoc, pstrPos;

// Read in the edit document iLength = GetWindowTextLength(hwndEdit);

if(NULL ==(pstrDoc =(PSTR) malloc(iLength + 1))) return FALSE;

GetWindowText(hwndEdit, pstrDoc, iLength + 1);

// Search the document for the find string pstrPos = strstr(pstrDoc + *piSearchOffset, pfr->lpstrFindWhat);

free(pstrDoc);

// Return an error code if the string cannot be found if(pstrPos == NULL) return FALSE;

// Find the position in the document and the new start offset iPos = pstrPos - pstrDoc;

*piSearchOffset = iPos + strlen(pfr->lpstrFindWhat);

// Select the found text SendMessage(hwndEdit, EM_SETSEL, iPos, *piSearchOffset);

SendMessage(hwndEdit, EM_SCROLLCARET, 0, 0);

return TRUE;

} BOOL PopFindNextText(HWND hwndEdit, int *piSearchOffset) { FINDREPLACE fr;

fr.lpstrFindWhat = szFindText;

return PopFindFindText(hwndEdit, piSearchOffset, &fr);

} BOOL PopFindReplaceText(HWND hwndEdit, int *piSearchOffset, LPFINDREPLACE pfr) { // Find the text if(!PopFindFindText(hwndEdit, piSearchOffset, pfr)) return FALSE;

// Replace it SendMessage(hwndEdit, EM_REPLACESEL, 0,(LPARAM) pfr->lpstrReplaceWith);

return TRUE;

} BOOL PopFindValidFind(void) { return *szFindText != '\0';

} POPFONT.C /*------------------------------------------ POPFONT.C -- Popup Editor Font Functions ------------------------------------------*/ #include #include static LOGFONT logfont;

static HFONT hFont;

BOOL PopFontChooseFont(HWND hwnd) { CHOOSEFONT cf;

cf.lStructSize = sizeof(CHOOSEFONT);

cf.hwndOwner = hwnd;

cf.hDC = NULL;

cf.lpLogFont = &logfont;

cf.iPointSize = 0;

cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS;

cf.rgbColors = 0L;

cf.lCustData = 0L;

cf.lpfnHook = NULL;

cf.lpTemplateName = NULL;

cf.hInstance = NULL;

cf.lpszStyle = NULL;

cf.nFontType = 0;

// Returned from ChooseFont cf.nSizeMin = 0;

cf.nSizeMax = 0;

return ChooseFont(&cf);

} void PopFontInitialize(HWND hwndEdit) { GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), (PSTR) &logfont);

hFont = CreateFontIndirect(&logfont);

SendMessage(hwndEdit, WM_SETFONT,(WPARAM) hFont, 0);

} void PopFontSetFont(HWND hwndEdit) { HFONT hFontNew;

RECT rect;

hFontNew = CreateFontIndirect(&logfont);

SendMessage(hwndEdit, WM_SETFONT,(WPARAM) hFontNew, 0);

DeleteObject(hFont);

hFont = hFontNew;

GetClientRect(hwndEdit, &rect);

InvalidateRect(hwndEdit, &rect, TRUE);

} void PopFontDeinitialize(void) { DeleteObject(hFont);

} POPPRNT0.C /*--------------------------------------------------------------- POPPRNT0.C -- Popup Editor Printing Functions(dummy version) ---------------------------------------------------------------*/ #include BOOL PopPrntPrintFile(HINSTANCE hInst, HWND hwnd, HWND hwndEdit, PSTR pstrTitleName) { return FALSE;

} POPPAD.RC /*--------------------------- POPPAD.RC resource script ---------------------------*/ #include #include "poppad.h" PopPad ICON "poppad.ico" PopPad MENU { POPUP "&File" { MENUITEM "&New\tCtrl+N", IDM_NEW MENUITEM "&Open...\tCtrl+O", IDM_OPEN MENUITEM "&Save\tCtrl+S", IDM_SAVE MENUITEM "Save &As...", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "&Print...\tCtrl+P", IDM_PRINT MENUITEM SEPARATOR MENUITEM "E&xit", IDM_EXIT } POPUP "&Edit" { MENUITEM "&Undo\tCtrl+Z", IDM_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t\tCtrl+X", IDM_CUT MENUITEM "&Copy\tCtrl+C", IDM_COPY MENUITEM "&Paste\tCtrl+V", IDM_PASTE MENUITEM "De&lete\tDel", IDM_CLEAR MENUITEM SEPARATOR MENUITEM "&Select All", IDM_SELALL } POPUP "&Search" { MENUITEM "&Find...\tCtrl+F", IDM_FIND MENUITEM "Find &Next\tF3", IDM_NEXT MENUITEM "R&eplace...\tCtrl+R", IDM_REPLACE } POPUP "F&ormat" { MENUITEM "&Font...", IDM_FONT } POPUP "&Help" { MENUITEM "&Help", IDM_HELP MENUITEM "&About PopPad...", IDM_ABOUT } } PopPad ACCELERATORS { "^N", IDM_NEW "^O", IDM_OPEN "^S", IDM_SAVE "^P", IDM_PRINT "^Z", IDM_UNDO VK_BACK, IDM_UNDO, VIRTKEY, ALT "^X", IDM_CUT VK_DELETE, IDM_CUT, VIRTKEY, SHIFT "^C", IDM_COPY VK_INSERT, IDM_COPY, VIRTKEY, CONTROL "^V", IDM_PASTE VK_INSERT, IDM_PASTE, VIRTKEY, SHIFT VK_DELETE, IDM_CLEAR, VIRTKEY "^F", IDM_FIND VK_F3, IDM_NEXT, VIRTKEY "^R", IDM_REPLACE VK_F1, IDM_HELP, VIRTKEY } AboutBox DIALOG 20, 20, 160, STYLE WS_POPUP | WS_DLGFRAME { CTEXT "PopPad" -1, 0, 12, 160, ICON "PopPad" -1, 8, 8, 0, CTEXT "Popup Editor for Microsoft Windows" -1, 0, 36, 160, CTEXT "Copyright(c) Charles Petzold, 1996" -1, 0, 48, 160, DEFPUSHBUTTON "OK" IDOK, 64, 60, 32, 14, WS_GROUP } PrintDlgBox DIALOG 20, 20, 100, STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE CAPTION "PopPad" { CTEXT "Sending", -1, 0, 10, 100, CTEXT "", IDD_FNAME, 0, 20, 100, CTEXT "to print spooler.", -1, 0, 30, 100, DEFPUSHBUTTON "Cancel", IDCANCEL, 34, 50, 32, 14, WS_GROUP } POPPAD.H /*---------------------- POPPAD.H header file ----------------------*/ #define IDM_NEW #define IDM_OPEN #define IDM_SAVE #define IDM_SAVEAS #define IDM_PRINT #define IDM_EXIT #define IDM_UNDO #define IDM_CUT #define IDM_COPY #define IDM_PASTE #define IDM_CLEAR #define IDM_SELALL #define IDM_FIND #define IDM_NEXT #define IDM_REPLACE #define IDM_FONT #define IDM_HELP #define IDM_ABOUT #define IDD_FNAME POPPAD.ICO Рис. 11.11 Программа POPPAD Чтобы в главе 15 повторно не воспроизводить исходный код программы, в меню была добавлена опция печати и поддержка некоторых других возможностей, что отражено в файле POPPAD.RC.

Все основные исходные программы записаны в файл POPPAD.С. В файле POPFILE.C находится код вызова окон диалога File Open и File Save, а также функции ввода/вывода. Файл POPFIND.C содержит логику поиска и замены, а файл POPFONT.C — логику выбора шрифта. В файле POPPRNT0.C почти ничего нет: в главе 15, при создании окончательной версии программы POPPAD он будет заменен на файл POPPRNT.C.

Рассмотрим сначала файл POPPAD.С. Обратите внимание, что параметр szCmdLine функции WinMain записывается последним при вызове функции CreateWindow. Эта строка могла бы содержать имя файла, если бы, программа POPPAD3 запускалась из командной строки с параметром. В WndProc при обработке сообщения WM_CREATE, заданное имя файла передается функции PopFileRead, которая находится в файле POPFILE.C.

В файле POPPAD.С поддерживаются две строки для хранения имени файла: в первой с именем szFileName, расположенной в WndProc, полностью задается диск, путь и имя файла. Во второй с именем szTitleName задается только само имя. В программе POPPAD3 она используется в функции DoCaption для вывода в заголовке окна имени файла;

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

В файле POPFILE.C находятся несколько функций для вывода на экран окон диалога File Open и File Save, а также фактической реализации ввода/вывода файлов. Окна диалога выводятся на экран с помощью функций GetOpenFileName и GetSaveFileName, которые находятся в динамически подключаемой библиотеке диалоговых окон общего пользования (COMDLG32.DLL). Обе эти функции используют структуру типа OPENFILENAME, определяемую в заголовочном файле COMMDLG.H. В файле POPFILE.C для этой структуры используется глобальная переменная ofn. Большинство полей структуры ofn инициализируются в функции PopFileInitialize, которая вызывается в файле POPPAD.С при обработке в WndProc сообщения WM_CREATE.

Структурную переменную ofn удобно сделать статической и глобальной, поскольку функции GetOpenFileName и GetSaveFileName возвращают в структуру некоторую информацию, которая необходима при последующих вызовах этих функций. Хотя в диалоговых окнах общего пользования имеется множество опций, включая возможность задания собственного шаблона окна диалога и проникновения в процедуру окна диалога, в файле POPPAD.С используются только базовые возможности диалоговых окон File Open и File Save. Задаваемыми полями структуры OPENFILENAME являются: lStructSize (размер структуры), hwndOwner (владелец окна диалога), lpstrFilter (о котором разговор впереди), lpstrFile и nMaxFile (указатель на буфер, в котором задано полное, с учетом пути, имя файла и размер этого буфера), lpstrFileTitle и nMaxFileTitle (буфер и его размер только для имени файла), Flags (для установки опций окна диалога) и lpstrDefExt (здесь задается строка текста с заданным по умолчанию расширением имени файла, если пользователь не задает собственное при наборе имени файла в окне диалога).

Когда пользователь выбирает опцию Open меню File, программа POPPAD3 вызывает функцию PopFileOpenDlg из файла POPFILE.C, при этом функции передаются описатель окна, указатель на буфер полного имени файла и указатель на буфер только имени файла. Функция PopFileOpenDlg помещает в структуру OPENFILENAME соответственно поля hwndOwner, lpstrFile и lpstrFileTitle, устанавливает Flags в значение OFN_HIDEREADONLY | OFN_CREATEPROMPT, а затем вызывает функцию GetOpenFileName, которая выводит на экран хорошо нам знакомое окно диалога, показанное на рис. 11.12. В заданном по умолчанию окне диалога File Open имеется флажок, который дает пользователю возможность назначить, что файл должен быть открыт только для чтения;

при установке флага OFN_HIDEREADONLY функция GetOpenFileName не выводит этот флажок на экран.

Когда пользователь закрывает показанное на рисунке окно диалога, функция GetOpenFileName завершает свою работу. Если заданного пользователем файла не существует, то флаг OFN_CREATEPROMPT заставляет функцию GetOpenFileName выводить на экран окно сообщения, в котором пользователю задается вопрос о необходимости создания нового файла.

Рис. 11.12 Окно диалога File Open В поле комбинированного списка, расположенном в левом нижнем углу окна диалога, перечислены типы файлов, которые будут выведены в окне. Это поле называют фильтром (filter). Пользователь может изменить фильтр, выбрав в комбинированном списке другой тип файлов. В функции PopFileInitialize из файла POPFILE.C в переменной szFilter фильтр определен для трех типов файлов: текстовых с расширением.TXT, файлов ASCII с расширением.ASC и файлов всех типов. Это значение устанавливается в поле lpstrFilter структуры OPENFILENAME.

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

Аналогично работает функция PopFileSaveDlg из файла POPFILE.C. Она устанавливает параметр Flags в OFN_OVERWRITEPROMPT и для вывода на экран окна диалога File Save вызывает функцию GetSaveFileName. Если выбранный пользователем файл уже существует, флаг OFN_OVERWRITEPROMPT приводит к выводу на экран окна сообщения, с запросом о необходимости перезаписать существующий файл. Другие функции файла POPFILE.C реализуют ввод/вывод файлов, используя при этом стандартные библиотечные функции языка С. (О вводе/выводе файлов в Windows 95 подробно будет рассказано в главе 13.) Изменение шрифта Как обеспечить пользователя возможностью легко выбирать себе шрифты показано в файле POPFONT.C.

При обработке сообщения WM_CREATE в программе POPPAD3 вызывается функция PopFontInitialize из файла POPFONT.C. Эта функция получает структуру LOGFONT, сформированную на основе системного шрифта, создает на ее базе шрифт и для установки нового шрифта посылает дочернему окну редактирования сообщение WM_SETFONT. (Хотя заданным по умолчанию шрифтом дочернего окна редактирования является системный шрифт, функция PopFontInitialize создает для него новый дополнительный шрифт, поскольку в конечном итоге все равно шрифт будет удален, что для стандартного системного шрифта было бы неразумным.) Когда программа POPPAD3 при выборе опции шрифта получает сообщение WM_COMMAND, вызывается функция PopFontChooseFont. Эта функция инициализирует структуру CHOOSEFONT, а затем для вывода на экран диалогового окна выбора шрифта вызывает функцию ChooseFont. Если пользователь нажимает кнопку OK, возвращаемым значением функции ChooseFont будет TRUE. Тогда программа POPPAD3 вызывает функцию PopFontSetFont для установки в дочернем окне редактирования нового шрифта. Старый шрифт удаляется.

И наконец, при обработке сообщения WM_DESTROY, программа POPPAD3 вызывает функцию PopFontDeinitialize для удаления последнего шрифта, созданного функцией PopFontSetFont.

Поиск и замена Библиотека диалоговых окон общего пользования включает в себя также два окна диалога для выполнения функций поиска и замены текста. В обеих этих функциях (FindText и ReplaceText) используется структура типа FINDREPLACE. В файле POPFIND.C, приведенном на рис. 11.11, для вызова этих функций имеются две других функции (PopFindFindDlg и PopFindReplaceDlg);

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

Имеется несколько замечаний, связанных с использованием функций поиска и замены. Во-первых, окна диалога, которые они вызывают, являются немодальными, что означает в случае активных окон диалога, необходимо изменять цикл обработки сообщений, чтобы вызвать функцию IsDialogMessage. Во-вторых, структура FINDREPLACE, передаваемая функциям FindText и ReplaceText, должна быть задана как статическая переменная;

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

В-третьих, до тех пор, пока окна диалога остаются на экране, функции FindText и ReplaceText взаимодействуют с окном-владельцем посредством специального сообщения. Номер этого сообщения может быть получен с помощью вызова функции RegisterWindowMessage с параметром FINDMSGSTRING. Это делается при обработке в WndProc сообщения WM_CREATE, и полученный номер сообщения сохраняется в статической переменной.

При обработке очередного сообщения в WndProc переменная сообщения сравнивается со значением, возвращаемым функцией RegisterWindowMessage. Параметр lParam сообщения — это указатель на структуру FINDREPLACE, поле Flags которое показывает, использовал ли пользователь окно диалога для поиска и замены текста или оно закрывается. Для непосредственной реализации поиска и замены в программе POPPAD вызываются функции PopFindFindDlg и PopFindReplaceDlg, находящиеся в файле POPFIND.C.

Программа для Windows, содержащая всего один вызов функции К настоящему времени нами были созданы две программы, в которых имеется возможность просматривать выбираемые цвета: программа COLORS1 в главе 8 и программа COLORS2 в этой главе. Теперь настал черед программы COLORS3, в которой функция Windows вызывается только один раз. Исходный код программы COLORS3 представлен на рис. 11.13.

Единственной функцией Windows, которая вызывается в программе COLORS3, является функция ChooseColor — это еще одна функция библиотеки диалоговых окон общего пользования. Окно диалога, которое она выводит на экран, показано на рис. 11.14. Процесс подбора цветов похож на тот, который был в программах COLORS1 и COLORS2, но он несколько более интерактивный.

COLORS3.MAK #----------------------- # COLORS3.MAK make file #----------------------- colors3.exe : colors3.obj $(LINKER) $(GUIFLAGS) -OUT:colors3.exe colors3.obj $(GUILIBS) colors3.obj : colors3.c $(CC) $(CFLAGS) colors3.c COLORS3.C /*---------------------------------------------- COLORS3.C -- Version using Common Dialog Box (c) Charles Petzold, ----------------------------------------------*/ #include #include int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static CHOOSECOLOR cc;

static COLORREF crCustColors[16];

cc.lStructSize = sizeof(CHOOSECOLOR);

cc.hwndOwner = NULL;

cc.hInstance = NULL;

cc.rgbResult = RGB(0x80, 0x80, 0x80);

cc.lpCustColors = crCustColors;

cc.Flags = CC_RGBINIT | CC_FULLOPEN;

cc.lCustData = 0L;

cc.lpfnHook = NULL;

cc.lpTemplateName = NULL;

return ChooseColor(&cc);

} Рис. 11.13 Программа COLORS В функции ChooseColor используется структура типа CHOOSECOLOR, а для хранения выбранных пользователем в окне диалога цветов — массив из 16 элементов типа COLORREF (DWORD). Поле rgbResult может быть инициализировано значением того цвета, который в дальнейшем появится на экране, если в поле Flags установлен флаг CC_RGBINIT. При нормальном использовании функции, в поле rgbResult устанавливается тот цвет, который выбирает пользователь.

Рис. 11.14 Вид экрана программы COLORS Обратите внимание, что поле hwndOwner окна диалога Color устанавливается в NULL. Когда функция ChooseColor для вывода на экран окна диалога вызывает функцию DialogBox, третий параметр функции DialogBox также устанавливается в NULL. Такая установка совершенно нормальна. Она означает, что у окна диалога нет родительского окна. В панели задач Windows 95 появится заголовок диалогового окна, и будет казаться, что окно диалога функционирует во многом так, как обычное окно.

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

Глава 12 Современный пользовательский интерфейс Каждая вновь выпущенная версия Windows предлагала улучшенный вариант пользовательского интерфейса, и Windows 95 — не исключение. Она предлагает целый набор усовершенствований, облегчающих работу пользователя. Различные формы программы Windows Explorer (включающие Network Neighborhood и Control Panel) упрощают просмотр жестких дисков, сетевых ресурсов, и окон, управляющих системными настройками.

Новая оболочка включает старую файловую систему MS-DOS в область имен, содержащей сетевые файлы и серверы печати. Наиболее очевидная часть файловой системы — это рабочий стол (desktop) (указываемый в командной строке как \WINDOWS\DESKTOP), который представляет собой настроенное пользователем окно на доступные программы и файлы. Другой очевидной частью файловой системы является меню Start (расположенное в каталоге \WINDOWS\Start Menu), поддерживающее иерархические меню, как альтернативу рабочему столу в элементизации программ и файлов данных.

Для упрощения создания Windows — программ с интерфейсом пользователя, соответствующим элегантному интерфейсу оболочки системы, Microsoft разработала библиотеку элементов управления общего пользования (common control library). Из семнадцати элементов управления общего пользовани некоторые — такие, как панель инструментов (toolbar) и строка состояния (status bar) — уже много лет используются во многих приложениях, созданных разработчиками, способными самостоятельно создавать программные средства (или использовать набор средств, поддерживаемых библиотеками классов, такими как Microsoft Foundation Class (MFC) Library или OWL фирмы Borland). Другие элементы управления общего пользования, такие как иерархическое дерево просмотра (tree view) и конфигурируемое окно списка (list view), были введены впервые в Windows 95. На рис. 12.1 обобщена информация об элементах управления общего пользования, разделенных на четыре категории: элементы управления главного окна (frame controls), составные диалоговые элементы управления (compound dialog controls), элементы управления Windows Explorer (Windows Explorer controls) и другие элементы управления (miscellaneous controls).

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

Toolbar (панель инструментов) Состоит из кнопок быстрого доступа.

Tooltip (окно подсказки) Обеспечивает пользователя быстрой подсказкой, отображая текст во всплывающем окне.

Status bar (строка состояния) Информационная строка, обычно размещаемая в нижней части окна приложения.

Составные диалоговые Элементы управления для списков свойств и элементы управления мастеров (wizards).

Property page (страница свойств) Немодальное диалоговое окно, используемое как одна страница в списке свойств или мастере.

Property sheet (набор страниц Набор из множества окон страниц свойств.

свойств) Элементы управления Элементы управления для построения Windows Explorer приложений, похожих на Windows Explorer.

Tree view (дерево просмотра) Отображает иерархически элементизированный список (левая панель окна программы Windows Explorer).

Категория Элемент Описание управления List view (список просмотра) Отображает список элементов, идентифицируемых битовым образом и текстовыми данными (правая панель окна программы Windows Explorer).

Другие элементы управления Animation (анимационное Проигрывает анимационную изображение) последовательность для индикации длительной операции.

Drag list (список, Окно списка, поддерживающее поддерживающий операции простые операции drag/drop по отношению к себе типа drag/drop) и другим окнам типа Drag list. (Не drag/drop OLE-контейнер).

Header (заголовок списка Отображает горизонтальные заголовки для просмотра) столбцов (используется совместно со списком просмотра).

Hot-Key (горячая клавиша) Отображает результат операции определения клавиш активизации (горячих клавиш).

Image list (список изображений) Элемент управления для хранения набора растровых изображений (битовых образов, курсоров, значков), не являющийся окном.

Progress bar (индикатор Отображает динамику длительной операции как процесса) процент от выполненной задачи.

Rich edit (усовершенствованный Редактор, поддерживающий множество шрифтов редактор) и базовые возможности OLE-контейнера.

Tab (набор закладок для выбора) Отображает список закладок для выбора. Tabs используются в окне набора страниц свойств для выбора страницы свойств. Панель задач (task bar) Windows 95 — есть элемент управления Tab, использующий кнопки вместо закладок.

Trackbar (окно с движком для Тип полосы прокрутки для выбора значения в выбора значения из диапазона) заданном диапазоне.

Up-Down (полоса прокрутки, Тип полосы прокрутки, состоящий из двух связанная с окном стрелок (но собственно без полосы) для редактирования для увеличения увеличения или или уменьшения на 1 уменьшения на 1 величины, целочисленного значения) находящейся в связанном поле редактирования.

Рис. 12.1 Элементы управления, поддерживаемые библиотекой COMCTL32.DLL В этой главе мы рассмотрим основы работы со всеми этими элементами управления и остановимся на первых двух категориях — элементах управления главного окна и составных элементах управления — представляющих собой набор, который будет использоваться в каждом приложении для Windows. Две другие категории — элементы управления Windows Explorer и другие элементы управления — более детально рассматриваются в книге Nancy Cluts "Programming the Windows 95 User Interface" (Microsoft Press, 1995).

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

Разница между элементами управления общего пользования и предопределенными элементами управления состоит в том, какие сообщения они посылают для уведомления. Предопределенные элементы управления посылают уведомляющие сообщения WM_COMMAND, в то время как элементы управления общего пользования (за некоторыми исключениями) посылают сообщения WM_NOTIFY. Хотя при поверхностном взгляде механизмы передачи отличаются, идея, лежащая в основе этих уведомляющих сообщений, одна и та же: родительское окно может иметь возможность реагировать на все интересующие его события.

Наиболее важно при работе с элементами управления обоих типов помнить, что элемент управления — это окно, и все, что вы уже знаете о манипулировании окнами, применимо при манипулировании элементами управления.

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

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

Инициализация библиотеки Все элементы управления общего пользования, за исключением усовершенствованного редактора, находятся в файле COMCTL32.DLL, который впервые появился в Microsoft Windows NT 3.51 и Windows 95. (По имени этой DLL можно было бы предположить, что где-то существует 16-разрядная версия этой библиотеки. Microsoft сообщала, что не планирует выпуск 16-битной версии библиотеки элементов управления общего пользования. Microsoft сделала доступными некоторые элементы похожей библиотеки элементов управления общего пользования, поставляемой с Microsoft Windows for Workgroups 3.11, но эта библиотека никогда не поддерживалась официально, и не входит в существующие версии операционных систем Microsoft.) Для использования какого-либо элемента управления общего пользования программа сначала вызывает функцию InitCommonControls, которая регистрирует классы окон элементов управления, используя функцию RegisterClass.

Функция InitCommonControls не имеет параметров и не возвращает никакого значения:

InitCommonControls();

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

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

error: unresolved external symbol impInitCommonControls@ Для решения этой проблемы добавьте файл COMCTL32.LIB в список компонуемых библиотек.

Ввиду размера и сложности усовершенствованного редактора, он располагается в его собственной динамически подключаемой библиотеке RICHED32.DLL. (Microsoft не собирается выпускать библиотеку RICHED16.DLL.) Усовершенствованный редактор регистрирует самого себя при загрузке этой библиотеки, которую вы вызываете посредством функции LoadLibrary:

LoadLibrary( "RICHED32.DLL");

Описания усовершенствованного редактора находятся в файле RICHEDIT.H, а связанные с OLE описания для него находятся в файле RICHOLE.H.

Создание элементов управления общего пользования Наиболее общий путь создания окна элемента управления общего пользования состоит в вызове функции CreateWindow или CreateWindowEx. (Функция CreateWindowEx идентична функции CreateWindow, с тем исключением, что она использует дополнительные стили. Эти стили окна рассматриваются в следующем разделе.) Например, приведенный вызов строит панель инструментов:

HWND hwndToolBar = CreateWindow( TOOLBARCLASSNAME, NULL, CCS_TOP | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndParent, (HMENU) 1, hInst, );

Рассмотрение некоторых параметров мы пока отложим. Имя класса окна не задается в кавычках, поскольку это символическая константа, определение которой зависит от набора символов, выбранного при построении программы. Для набора символов ANSI символическая константа TOOLBARCLASSNAME заменяется строкой "ToolbarWindow32";

для набора символов UNICODE символ "L" ставится перед этим именем (L"ToolbarWindow32") для создания UNICODE-строки. Все классы элементов управления общего пользования определяются этим способом.

Чаще всего элементы управления общего пользования создаются как дочерние окна, что определяется заданием флага WS_CHILD и установкой описателя родительского окна hwndParent. Как показано в примере, дочерние окна часто создаются с начальным местоположением (x, y) и размерами (cx, cy) равными нулю, а затем изменяют свой размер при изменении размеров родительского окна (т. е. когда родительское окно получает сообщение WM_SIZE).

Альтернативой вызову функции CreateWindow является вызов специализированной функции создания элемента управления, которая обычно выполняет некоторую стандартную инициализацию. Примером специализированной функции создания элемента управления является функция CreateToolbarEx, строящая панель инструментов, и добавляющая в нее кнопки. В других случаях, таких как набор страниц свойств и страница свойств, имя класса недоступно, поэтому требуется вызов специализированной функции: PropertySheet строит набор страниц свойств, а CreatePropertySheetPage строит индивидуальные страницы свойств. Список изображений строится вызовом функции ImageList_Create — специализированной функции, поскольку список изображений не является окном. На рис. 12.2 приведены все имена классов элементов управления общего пользования и функции их создания.

Категория/Элемент управления Класс элемента управления Функция создания Элементы управления главного окна Панель инструментов TOOLBARCLASSNAME CreateToolbarEx Окно подсказки TOOLTIPS_CLASS Нет Строка состояния STATUSCLASSNAME CreateStatusWindow Анимационное изображение ANIMATE_CLASS Нет Индикатор процесса PROGRESS_CLASS Нет Составные диалоговые элементы управления Страница свойств Нет CreatePropertySheetPage Набор страниц свойств Нет PropertySheet Элементы управления Windows Explorer Дерево просмотра WC_TREEVIEW Нет Список просмотра WC_LISTVIEW Нет Список изображений Нет ImageList_Create Другие элементы управления Список, поддерживающий операции "listbox" (ANSI) или L"listbox" MakeDragList типа drag/drop (UNICODE) Заголовок списка просмотра WC_HEADER Нет Горячая клавиша HOTKEY_CLASS Нет Усовершенствованный редактор "RichEdit" (ANSI) или Нет L"RichEdit" (UNICODE) Набор закладок для выбора WC_TABCONTROL Нет Окно с движком для выбора значения TRACKBAR_CLASS Нет из диапазона Полоса прокрутки, связанная с окном UPDOWN_CLASS CreateUpDownControl редактирования для изменения значения Рис. 12.2 Имена классов и функции создания элементов управления общего пользования Стили элементов управления общего пользования Необходимо приложить немало усилий при создании любого типа окна — вашего собственного окна, предопределенного элемента управления или элемента управления общего пользования. Эта работа состоит в выборе правильных флагов стиля окна. (Вспомните, флаги стиля объединяются побитовой операцией OR языка C и передаются вместе как один из двух параметров в функцию CreateWindowEx : или как первый параметр dwExStyle, или как четвертый — dwStyle.) Эта работа достаточна трудна, поскольку флаги стиля влияют на широкий диапазон возможностей, включающих визуальное представление окна (или отсутствие отображения, если флаг WS_VISIBLE пропущен по невнимательности), поведение окна и конкретные типы взаимодействия между окнами.

При создании элементов управления общего пользования существует четыре набора флагов стиля: флаги основного стиля окна (WS_), флаги основного стиля элемента управления общего пользования (CCS_), флаги стиля, специфичные для конкретного элемента управления и флаги расширенного стиля (WS_EX_). Флаги первого из перечисленных типов передаются в функцию CreateWindowEx в качестве четвертого параметра;

флаги последнего типа передаются в функцию CreateWindowEx в качестве первого параметра.

Основные стили окна Основные стили окна имеют имена, начинающиеся с префикса WS_ и могут влиять на окна любого класса. Из примерно двадцати этих стилей окна семь применяются к элементам управления общего пользования: WS_CHILD, WS_VISIBLE, WS_DISABLED, WS_BORDER, WS_TABSTOP, WS_CLIPCHILDREN и WS_CLIPSIBLINGS.

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

Флаг стиля WS_VISIBLE позволяет окну быть отображенным (тем не менее наличие этого флага не гарантирует того, что окно не сможет быть перекрыто другим окном). Частой программной ошибкой является отсутствие флага стиля WS_VISIBLE, что заставляет программиста повсюду (в книгах, журналах, службах поддержки online) искать причину того, что окно не отображается ("потерялось"). Изменяйте, если необходимо, видимость окна после его создания с помощью функций ShowWindow или SetWindowPos.

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

Наиболее общее использование этого флага состоит в запрещении элемента управления в диалоговом окне;

будучи запрещенными, большинство элементов управления изменяют свой вид, чтобы дать пользователю понять, что они в данный момент недоступны. Например, кнопка OK в диалоговом окне File Open запрещена до тех пор, пока в поле редактирования имени файла — пусто. Кнопка становится разрешенной (доступной), когда какой-либо текст введен в окно задания имени файла. Окно создается один раз с использованием функции CreateWindow, а вызовы функций EnableWindow позволяют делать это окно запрещенным или разрешенным.

Флаг WS_BORDER вызывает появление рамки вокруг окна элемента управления.

Если элемент управления находится в диалоговом окне, то использование флага WS_TABSTOP включает его в список переходов по клавише .

Флаги WS_CLIPCHILDREN и WS_CLIPSIBLINGS защищают поверхность дочерних окон от внешнего разрушения.

Термин "отсечение" (clipping) относится к прорисовке границ между окнами. В то время, как отсечение всегда разрешено для перекрывающихся и всплывающих окон, оно запрещено для дочерних окон. Флаг WS_CLIPCHILDREN разрешает отсечение, когда рисуется родительское окно дочерних окон, тем самым предотвращая рисование в родительском окне на дочерних окнах. Флаг WS_CLIPSIBLINGS разрешает отсечение между дочерними окнами, имеющими общее родительское окно, для предотвращения соперничества между ними — борьбы между двумя дочерними окнами, имеющими общего родителя, за пиксели в перекрывающихся участках. При работе с дочерними окнами эти два стиля помогают решить проблемы непонятного визуального представления этих окон.

Флаги расширенного стиля окна Флаги расширенного стиля окна имеют имена, начинающиеся с префикса WS_EX_, и передаются в функцию CreateWindowEx в качестве первого параметра. Из 27 расширенных стилей 3 относятся к созданию дочерних окон:

WS_EX_CLIENTEDGE, WS_EX_STATICEDGE и WS_EX_NOPARENTNOTIFY.

Флаги стиля WS_EX_CLIENTEDGE и WS_EX_STATICEDGE поддерживают трехмерность изображения.

Надлежащее их использование позволяет приложению хорошо выглядеть по отношению к другим приложениям Windows 95. Обратите внимание, что эти флаги стиля поддерживаются только в Windows 95 и версиях Windows NT, имеющих интерфейс системы Windows 95. (Эти флаги не имеют никакого эффекта в более ранних версиях операционной системы.) Флаг стиля WS_EX_CLIENTEDGE строит "углубленную" область основной рабочей зоны приложения. Например, окно текста программы текстового процессора. Так как большинство элементов управления, таких как панель инструментов и строка состояния, создаются вне этой области, избегайте этих флагов стиля для родительских окон панели инструментов и строки статуса. Кроме того, избегайте использования флага стиля WS_EX_OVERLAPPEDWINDOW, который включает как часть своего определения флаг стиля WS_EX_CLIENTEDGE.

Флаг стиля WS_EX_STATICEDGE создает углубленное представление только для окон, в которые осуществляется вывод. Например, уведомляющее окно на панели задач Windows 95 — маленькое окно у правого края панели задач, содержащее значки строки состояния и часы — использует этот стиль. Этот флаг используется с такими элементами управления как индикатор процесса и окно анимационного изображения. Кроме того, элементы управления, предназначенные только для вывода, входящие в состав строки состояния или панели инструментов, будут, вероятно, использовать этот флаг стиля, так как его уникальное представление легко понимается опытным пользователем Windows 95.

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

Флаги основного стиля элемента управления общего пользования Библиотека элементов управления общего пользования поддерживает набор значений стиля с префиксом CCS_ для использования с панелями инструментов, окнами состояния и заголовками списка просмотра. Это флаги CCS_ADJUSTABLE, CCS_BOTTOM, CCS_NODIVIDER, CCS_NOMOVEY, CCS_NOPARENTALIGN, CCS_NORESIZE и CCS_TOP. Поскольку смысл этих флагов стиля зависит от конкретного элемента управления, при дальнейшем рассмотрении мы будем обращать ваше внимание на детали.

Флаги стиля, специфичные для конкретного элемента управления Элементы управления общего пользования Windows 95 так же как и пред- определенные элементы управления диалогового окна имеют специфичные флаги стиля, такие как BS_PUSHBUTTON, ES_MULTILINE, LBS_SORT. Так же как и предопределенные элементы управления, каждый стиль элемента управления общего пользования имеет уникальный префикс. Все эти префиксы приведены в таблице на рис. 12.3. Более простые элементы управления общего пользования не имеют специфичных флагов стиля.

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

Категория/Элемент управления Префикс флага стиля Пример Элементы управления главного окна Панель инструментов TBSTYLE_ TBSTYLE_ALTDRAG Окно подсказки Нет Строка состояния SBARS_ SBARS_SIZEGRIP Анимационное изображение ACS_ ACS_AUTOPLAY Индикатор процесса Нет Составные диалоговые элементы управления Страница свойств Нет Набор страниц свойств Нет Элементы управления Windows Explorer Дерево просмотра TVS_ TVS_HASBUTTONS Список просмотра LVS_ LVS_ALIGNLEFT Список изображений Нет Другие элементы управления Список, поддерживающий Нет операции типа drag/drop Заголовок списка просмотра HDS_ HDS_BUTTONS Горячая клавиша Нет Усовершенствованный редактор ES_ ES_DISABLENOSCROLL Набор закладок для выбора TCS_ TCS_BUTTONS Окно с движком для выбора значения TBS_ TBS_AUTOTICKS из диапазона Полоса прокрутки, связанная с окном UDS_ UDS_ALIGNLEFT редактирования для изменения значения Рис. 12.3 Префиксы флагов стилей элементов управления общего пользования Посылка сообщений элементам управления общего пользования После создания окна элемента управления общего пользования для управления его действиями ему посылаются сообщения. Как можно предположить, для этого требуется вызов функции SendMessage с ее традиционными четырьмя параметрами: описатель окна, идентификатор сообщения, значение wParam, значение lParam. Так же как существуют специфические флаги стилей элементов управления общего пользования, так и существуют специфические сообщения.

Альтернативой вызовам функции SendMessage является использование набора макросов языка C, определенных в файле COMMCTRL.H, которые получают специфичный для сообщения набор параметров, осуществляют необходимые преобразования (например, упаковка двух величин типа shorts в одну величину типа lParam), а затем вызывают функцию SendMessage. Возвращаемое значение с целью минимизации числа сообщений компилятора также преобразуется к нужному типу, поскольку значение типа LRESULT, возвращаемое функцией SendMessage, не совпадает с ожидаемым типом возвращаемого значения.

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

hItem =(HTREEITEM)SendMessage( hwndTV, TVM_INSERTITEM, 0,(LPARAM)(LPTV_INSERTSTRUCT)&tvis );

Ниже показано, как послать то же самое сообщение, используя макрос TreeView_InsertItem:

hItem = TreeView_InsertItem(hwndTV, &tvis);

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

(Если вам интересно использование набора аналогичных макросов для предопределенных элементов управления, то вы можете найти их определения в файле WINDOWSX.H. Макросы не документированы ни в одном файле помощи. Они достаточно хорошо сами себя объясняют и просты в использовании. Так же как и применение макросов для элементов управления общего пользования, так и использование макросов из файла WINDOWSX.H может сделать программу проще для написания и чтения.) Несмотря на то что такие макросы очень полезны, файлы Win32, к сожалению, включают определения лишь для половины элементов управления общего пользования. Файл COMMCTRL.H содержит лишь макроопределения для следующих классов: анимационное изображение, заголовок списка просмотра, список просмотра, набор страниц свойств (в файле PRSHT.H), дерево просмотра и набор закладок для выбора.

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

Рис. 12.4 Программа CTLMACRO обеспечивает быстрый доступ к макросам сообщений элементов управления общего пользования На прилагаемом компакт-диске находится также программа CTLMACRO, которая каталогизирует все макросы элементов управления общего пользования. Как показано на рис. 12.4, программа CTLMACRO элементизирует все макросы элементов управления общего пользования иерархически, и они доступны посредством простого дерева просмотра. Когда вы найдете сообщение, которое вам необходимо, нажмите кнопку Copy для копирования макроса в папку обмена Clipboard. Оттуда его легко вставить в программу, используя редактор, в котором разрабатывается программа.

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

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

Однако, не все уведомления элементов управления общего пользования приходят как сообщения WM_NOTIFY. В частности, панель инструментов, использующая сообщения WM_NOTIFY для большинства уведомлений, посылает сообщения WM_COMMAND, когда нажимается кнопка. Поскольку панель инструментов используется для поддержки выбора из меню, тот же код обработки сообщений WM_COMMAND, что для поддержки меню и быстрых клавиш, будет обрабатывать сообщения от панели инструментов. Еще одно исключение — полоса прокрутки, связанная с окном редактирования для изменения значения, которая также посылает сообщения WM_VSCROLL или WM_HSCROLL при нажатии на стрелки.

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

Код уведомления Описание NM_CLICK Пользователь сделал щелчок левой кнопкой мыши NM_DBLCLK Пользователь сделал двойной щелчок левой кнопкой мыши NM_KILLFOCUS Элемент управления потерял фокус ввода NM_OUTOFMEMORY Ошибка нехватки памяти NM_RCLICK Пользователь сделал щелчок правой кнопкой мыши NM_RDBLCLK Пользователь сделал двойной щелчок правой кнопкой мыши NM_RETURN Пользователь нажал клавишу NM_SETFOCUS Элемент управления получил фокус ввода Не все элементы управления общего пользования обязательно посылают каждое из этих уведомляющих сообщений. Например, набор закладок для выбора не посылает уведомлений, связанных с изменением фокуса ввода (NM_KILLFOCUS и NM_SETFOCUS). При работе с каждым элементом управления общего пользования следует разобраться с тем, какие уведомляющие сообщения он посылает.

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

Можно обрабатывать сообщение WM_NOTIFY так, как показано ниже, для вывода информации в окно отладчика с помощью функции OutputDebugString. Для просмотра результатов работы этой функции в Win32 API вам следует запустить программу в отладчике:

case WM_NOTIFY:

{ int idCtrl =(int) wParam;

LPNMHDR pnmh =(LPNMHDR) lParam;

#ifdef _DEBUG // вывод уведомляющих сообщений в окно отладчика LPSTR pText;

if( QueryNotifyText(pnmh->code, &pText) ) { OutputDebugString(pText);

OutputDebugString("\r\n");

} #endif [другие строки программы] return 0;

} Каждый пример программы в этой главе обрабатывает сообщение WM_NOTIFY так, как показано выше.

Описания, которые необходимы для вызова функции QueryNotifyText, приведены ниже:

typedef struct tagCONTROLNOTIFICATIONS { UINT nCode;

LPSTR pName;

} CONTROLNOTIFICATIONS;

BOOL QueryNotifyText(UINT nNotifyCode, LPSTR *pName);

Функция и определения данных для QueryNotifyText приведены ниже:

#include #include #include #include #include "notify.h" CONTROLNOTIFICATIONS cnLookupTable[] = { NM_OUTOFMEMORY, "NM_OUTOFMEMORY", NM_CLICK, "NM_CLICK", NM_DBLCLK, "NM_DBLCLK", NM_RETURN, "NM_RETURN", NM_RCLICK, "NM_RCLICK", NM_RDBLCLK, "NM_RDBLCLK", NM_SETFOCUS, "NM_SETFOCUS", NM_KILLFOCUS, "NM_KILLFOCUS", LVN_ITEMCHANGING, "LVN_ITEMCHANGING", LVN_ITEMCHANGED, "LVN_ITEMCHANGED", LVN_INSERTITEM, "LVN_INSERTITEM", LVN_DELETEITEM, "LVN_DELETEITEM", LVN_DELETEALLITEMS, "LVN_DELETEALLITEMS", LVN_BEGINLABELEDITA, "LVN_BEGINLABELEDITA", LVN_BEGINLABELEDITW, "LVN_BEGINLABELEDITW", LVN_ENDLABELEDITA, "LVN_ENDLABELEDITA", LVN_ENDLABELEDITW, "LVN_ENDLABELEDITW", LVN_COLUMNCLICK, "LVN_COLUMNCLICK", LVN_BEGINDRAG, "LVN_BEGINDRAG", LVN_BEGINRDRAG, "LVN_BEGINRDRAG", LVN_GETDISPINFOA, "LVN_GETDISPINFOA", LVN_GETDISPINFOW, "LVN_GETDISPINFOW", LVN_SETDISPINFOA, "LVN_SETDISPINFOA", LVN_SETDISPINFOW, "LVN_SETDISPINFOW", LVN_KEYDOWN, "LVN_KEYDOWN", HDN_ITEMCHANGINGA, "HDN_ITEMCHANGINGA", HDN_ITEMCHANGINGW, "HDN_ITEMCHANGINGW", HDN_ITEMCHANGEDA, "HDN_ITEMCHANGEDA", HDN_ITEMCHANGEDW, "HDN_ITEMCHANGEDW", HDN_ITEMCLICKA, "HDN_ITEMCLICKA", HDN_ITEMCLICKW, "HDN_ITEMCLICKW", HDN_ITEMDBLCLICKA, "HDN_ITEMDBLCLICKA", HDN_ITEMDBLCLICKW, "HDN_ITEMDBLCLICKW", HDN_DIVIDERDBLCLICKA, "HDN_DIVIDERDBLCLICKA", HDN_DIVIDERDBLCLICKW, "HDN_DIVIDERDBLCLICKW", HDN_BEGINTRACKA, "HDN_BEGINTRACKA", HDN_BEGINTRACKW, "HDN_BEGINTRACKW", HDN_ENDTRACKA, "HDN_ENDTRACKA", HDN_ENDTRACKW, "HDN_ENDTRACKW", HDN_TRACKA, "HDN_TRACKA", HDN_TRACKW, "HDN_TRACKW", TVN_SELCHANGINGA, "TVN_SELCHANGINGA", TVN_SELCHANGINGW, "TVN_SELCHANGINGW", TVN_SELCHANGEDA, "TVN_SELCHANGEDA", TVN_SELCHANGEDW, "TVN_SELCHANGEDW", TVN_GETDISPINFOA, "TVN_GETDISPINFOA", TVN_GETDISPINFOW, "TVN_GETDISPINFOW", TVN_SETDISPINFOA, "TVN_SETDISPINFOA", TVN_SETDISPINFOW, "TVN_SETDISPINFOW", TVN_ITEMEXPANDINGA, "TVN_ITEMEXPANDINGA", TVN_ITEMEXPANDINGW, "TVN_ITEMEXPANDINGW", TVN_ITEMEXPANDEDA, "TVN_ITEMEXPANDEDA", TVN_ITEMEXPANDEDW, "TVN_ITEMEXPANDEDW", TVN_BEGINDRAGA, "TVN_BEGINDRAGA", TVN_BEGINDRAGW, "TVN_BEGINDRAGW", TVN_BEGINRDRAGA, "TVN_BEGINRDRAGA", TVN_BEGINRDRAGW, "TVN_BEGINRDRAGW", TVN_DELETEITEMA, "TVN_DELETEITEMA", TVN_DELETEITEMW, "TVN_DELETEITEMW", TVN_BEGINLABELEDITA, "TVN_BEGINLABELEDITA", TVN_BEGINLABELEDITW, "TVN_BEGINLABELEDITW", TVN_ENDLABELEDITA, "TVN_ENDLABELEDITA", TVN_ENDLABELEDITW, "TVN_ENDLABELEDITW", TVN_KEYDOWN, "TVN_KEYDOWN", TTN_NEEDTEXTA, "TTN_NEEDTEXTA", TTN_NEEDTEXTW, "TTN_NEEDTEXTW", TTN_SHOW, "TTN_SHOW", TTN_POP, "TTN_POP", TCN_KEYDOWN, "TCN_KEYDOWN", TCN_SELCHANGE, "TCN_SELCHANGE", TCN_SELCHANGING, "TCN_SELCHANGING", TBN_GETBUTTONINFOA, "TBN_GETBUTTONINFOA", TBN_GETBUTTONINFOW, "TBN_GETBUTTONINFOW", TBN_BEGINDRAG, "TBN_BEGINDRAG", TBN_ENDDRAG, "TBN_ENDDRAG", TBN_BEGINADJUST, "TBN_BEGINADJUST", TBN_ENDADJUST, "TBN_ENDADJUST", TBN_RESET, "TBN_RESET", TBN_QUERYINSERT, "TBN_QUERYINSERT", TBN_QUERYDELETE, "TBN_QUERYDELETE", TBN_TOOLBARCHANGE, "TBN_TOOLBARCHANGE", TBN_CUSTHELP, "TBN_CUSTHELP", UDN_DELTAPOS, "UDN_DELTAPOS", PSN_SETACTIVE, "PSN_SETACTIVE", PSN_KILLACTIVE, "PSN_KILLACTIVE", PSN_APPLY, "PSN_APPLY", PSN_RESET, "PSN_RESET", PSN_HELP, "PSN_HELP", PSN_WIZBACK, "PSN_WIZBACK", PSN_WIZNEXT, "PSN_WIZNEXT", PSN_WIZFINISH, "PSN_WIZFINISH", PSN_QUERYCANCEL, "PSN_QUERYCANCEL" };

int NOTIFY_COUNT = sizeof(cnLookupTable) / sizeof(CONTROLNOTIFICATIONS);

//------------------------------------------------------------------- // QueryNotifyText: Convert notification codes into text (Преобразование уведомляющего кода в текст) //------------------------------------------------------------------- BOOL QueryNotifyText(UINT nNotifyCode, LPSTR *pName) { BOOL bFound = FALSE;

int iNotify;

for(iNotify = 0;

iNotify < NOTIFY_COUNT;

iNotify++) { if(cnLookupTable[iNotify].nCode == nNotifyCode) { *pName = cnLookupTable[iNotify].pName;

return TRUE;

} } // Unknown notification code(неизвестный уведомляющий код) *pName = "** Unknown **";

return FALSE;

} Элементы управления главного окна Три элемента управления общего пользования часто используются в главных окнах: панели инструментов, окна подсказки и строки состояния. На рис. 12.5 показаны примеры каждого из этих элементов управления, реализованных в программе GADGETS.

Рис. 12.5 Программа GADGETS, иллюстрирующая использование элементов управления общего пользования: панели инструментов, окна подсказки, строки состояния Панели инструментов Панель инструментов — это дочернее окно, обычно расположенное под меню программы, и состоящее из кнопок, соответствующих наиболее употребительным пунктам меню и опциям программы. Кнопки панели инструментов сами по себе не являются окнами, они являются графическими объектами, нарисованными с использованием битовых образов на поверхности окна панели инструментов.

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

Кроме кнопок панель инструментов может содержать другие дочерние окна элементов управления, такие как окно комбинированного списка (combo box). Создаются встроенные элементы управления с помощью вызова функции CreateWindow при задании окна панели инструментов как родительского. Как указано в книге Nancy Cuts "Programming the Windows 95 User Interface", реальным препятствием для помещения элементов управления в панель инструментов является резервирование достаточно большого пиксельного пространства. В панели инструментов могут также содержаться разделители между кнопками и встроенными элементами управления. Так же как и случае меню, когда разделители определяются в описании меню специальным флагом SEPARATOR, так и в случае панели инструментов разделители кнопок строятся с использованием кнопок панели инструментов стиля TBSTYLE_SEP. Дюжина или примерно столько разделителей требуется для резервирования места для окна комбинированного списка в панели инструментов.

Создание панели инструментов Панель инструментов создается либо путем вызова функции CreateWindow и задания имени класса TOOLBARCLASSNAME, либо путем вызова функции CreateToolbarEx, которая создает панель инструментов и инициализирует набор кнопок. Ниже приведен прототип функции CreateToolbarEx:

HWND CreateToolbarEx( HWND hwnd, DWORD ws, UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons, int iNumButtons, int dxButton, int dyButton, int dxBitmap, int dyBitmap, UINT uStructSize );

Первые три параметра используются в вызове функции CreateWindow, который осуществляет функция CreateToolbarEx: hwnd — это описатель родительского окна, ws — флаги стиля окна, wID — идентификатор дочернего окна панели инструментов.

Следующие три параметра используются для загрузки ресурса битового образа, который содержит рисунки всех кнопок (множество изображений упакованы в строку в один битовый образ): nBitmaps — число изображений в битовом образе, hBMInst и wBMID идентифицируют ресурс битового образа для загрузки.

Параметр lpButtons — это указатель на массив элементов типа TBBUTTON, iNumButtons — задает число элементов в массиве. Каждый элемент TBBUTTON определяет битовый образ, идентификатор команды, тип кнопки, ее начальное состояние.

Размер каждой кнопки основывается на базе размеров изображения битового образа (dxBitmap, dyBitmap).

Минимальная ширина кнопки равна dxBitmap+7 пикселей, минимальная высота кнопки — dyBitmap+7 пикселей.

Существует возможность задать значения dxBitmap и dyBitmap для установки размеров кнопок больше минимальных.

Если необходимости в этом нет, то значения dxBitmap и dyBitmap устанавливаются равными нулю.

Вариант панели инструментов основывается на размере структуры TBBUTTON, который задается последним параметром uStructSize, и должен равняться sizeof (TBBUTTON).

При создании панель инструментов устанавливает свой размер и местоположение в "правильные" значения:

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

Заставить панель инструментов изменить эти установки можно только путем модификации стилей окна. Как это делать — описано в следующем разделе.

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

Категория Флаг стиля Описание Представление CCS_NODIVIDER Запрещает рисование разделительной линии над панелью инструментов TBSTYLE_WRAPABLE Поддерживает панели инструментов, состоящую из нескольких строк Автоматическое размещение CCS_TOP Помещает панель инструментов в верхнюю по оси y часть родительского окна (по умолчанию), выравнивая ширину по родительскому окну и высоту по размерам кнопки.

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

CCS_NOMOVEY Устанавливает начальное положение по оси x (у левой границы родительского окна), но не устанавливает начальное положение по оси y, выравнивая ширину по рабочей зоне родительского окна и высоту по размерам кнопки.

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

CCS_NORESIZE Запрещает все автоматические перемещения и изменения размеров.Это запрещает следующие флаги стиля: CCS_TOP, CCS_BOTTOM, CCS_NOMOVEY и CCS_NOPARENTALIGN. Вы должны явно задать размеры и положение панели инструментов.

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

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

Поддержка окон подсказки TBSTYLE_TOOLTIPS Строит элемент управления окно подсказки.

При создании панели инструментов устанавливается по умолчанию только стиль CCS_TOP, что приводит к расположению панели инструментов в верхней части родительского окна. После этого, родительское окно может сделать запрос панели инструментов для изменения ее размеров и местоположения посредством посылки сообщения TB_AUTOSIZE, что обычно и делает родительское окно при обработке сообщения WM_SIZE. За исключением посылки сообщения с запросом об изменении размера панель инструментов с флагом CCS_TOP или CCS_BOTTOM достаточно самостоятельна и не требует другого обслуживания. Присутствие флагов стиля, которые запрещают свойства автоматического перемещения и автоматического изменения размеров панели инструментов, требует немного больших затрат для обслуживания панели инструментов при изменении размеров родительского окна.

Два флага стиля изменяют представление панели инструментов. Первый, CCS_NODIVIDER, удаляет разделительную линию, призванную отделить кнопки панели инструментов стиля CCS_TOP от меню приложения.

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

Три флага стиля управляют перемещением относительно оси y: CCS_TOP (по умолчанию), CCS_BOTTOM и CCS_NOMOVEY. Эти флаги определяют, как будет расположена панель инструментов в ее родительском окне как при создании, так и при получении сообщения TB_AUTOSIZE. Флаг CCS_NOMOVEY делает возможным только приведение ширины (для полного использования рабочей области родительского окна), высоты и месторасположения относительно оси x, и оставляет на ваше усмотрение перемещение панели инструментов вдоль оси y, как при указании значений по оси y в вызове функции CreateWindow, так и при вызове функций типа MoveWindow после того, как панель инструментов будет создана. Панель инструментов с этим флагом была бы удобна в качестве второй панели инструментов, расположенной непосредственно под панелью инструментов с флагом CCS_TOP.

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

Флаг стиля CCS_ADJUSTABLE строит панель инструментов, которую пользователь может изменять "на лету". Кнопки могут быть перемещены в рамках панели инструментов или перетащены за панель инструментов с помощью клавиши и левой кнопки мыши. Двойной щелчок левой кнопкой мыши приводит к вызову диалогового окна изменения конфигурации панели инструментов (Customize Toolbar dialog box) для добавления, удаления или перемещения кнопок.

(Это диалоговое окно появляется и при получении панелью инструментов сообщения TB_CUSTOMIZE.) Изменение конфигурации требует от родительского окна реагирования на некоторые уведомляющие сообщения — TBN_QUERYINSERT и TBN_QUERYDELETE, посылаемые среди других панелью инструментов, которые запрашивают разрешение на вставку или удаление кнопок. Для панели инструментов с изменяемой конфигурацией флаг стиля TBSTYLE_ALTDRAG изменяет пользовательский интерфейс перемещения кнопок с сочетания клавиши и левой кнопки мыши на сочетание клавиши и левой кнопки мыши для тех случаев, когда первое сочетание используется в других целях.

Флаг стиля TBSTYLE_TOOLTIPS требует от панели инструментов создания элемента управления подсказка, который выводит маленькое окно с текстом для каждой кнопки. Родительское окно панели инструментов получает уведомление TTN_NEEDTEXT (в форме сообщения WM_NOTIFY), когда окну подсказки необходим текст для конкретной кнопки.

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

Битовые образы с более чем одним изображением должны помещать изображения в одну строку так, чтобы второе изображение находилось справа от первого, третье справа от второго и т. д. Функция CreateToolbarEx принимает только один идентификатор битового образа. Вы можете добавлять битовые образы с одним или более изображением кнопок путем посылки сообщения TB_ADDBITMAP панели инструментов.

Библиотека элементов управления общего пользования имеет два набора битовых образов, готовых для использования. Первый набор битовых образов содержит изображения кнопок, соответствующие командам меню File и Edit;

второй набор содержит изображения кнопок для различных типов просмотра. Каждый набор содержит два одинаковых ряда битовых образов двух размеров: большого (24х24) и маленького (16х16) пикселей. Для доступа к этим битовым образам необходимо задать специальное значение HINST_COMMCTRL в качестве описателя экземпляра ресурса битового образа (hBMInst) — параметра функции CreateToolbarEx (или в соответствующей структуре при посылке сообщения TB_ADDBITMAP). Значение параметра идентификатора битового образа wBMID выбирается из следующей таблицы:

Идентификатор ресурса битового образа Описание IDB_STD_SMALL_COLOR 16х16 изображения кнопок меню File и Edit IDB_STD_LARGE_COLOR 24х24 изображения кнопок меню File и Edit IDB_VIEW_SMALL_COLOR 16х16 изображения кнопок меню View IDB_VIEW_LARGE_COLOR 24х24 изображения кнопок меню View Два "стандартных" битовых образа (IDB_STD_SMALL_COLOR и IDB_STD_LARGE_COLOR) содержат по изображений. Связывание конкретного изображения с кнопкой на панели инструментов требует задания индекса изображения (относительно 0) в элементе iBitmap соответствующей кнопке структуры типа TBBUTTON. При использовании стандартных битовых образов выбирайте индексы из набора символических констант: STD_CUT, STD_COPY, STD_PASTE, STD_UNDO, STD_REDOW, STD_DELETE, STD_FILENEW, STD_FILEOPEN, STD_FILESAVE, STD_PRINTPRE, STD_PROPERTIES, STD_HELP, STD_FIND, STD_REPLACE, STD_PRINT.

Два битовых образа "вида" (view) (IDB_VIEW_SMALL_COLOR и IDB_VIEW_LARGE_COLOR) содержат по изображений. Выбирайте индексы изображений кнопок, используя следующие символические константы:

VIEW_LARGEICONS, VIEW_SMALLICONS, VIEW_LIST, VIEW_DETAILS, VIEW_SORTNAME, VIEW_SORTSIZE, VIEW_SORTDATE, VIEW_SORTTYPE, VIEW_PARENTFOLDER, VIEW_NETCONNECT, VIEW_NETDISCONNECT, VIEW_NEWFOLDER.

При использовании одного из этих четырех битовых образов нет необходимости устанавливать размеры (dxButton, dyButton) или размеры битового образа (dxBitmap, dyBitmap) поскольку функция CreateToolbarEx распознает эти битовые образы и устанавливает размеры сама.

Заполнение массива TBBUTTON Создание панели инструментов требует определения конкретных параметров кнопок. Вам необходимо заполнить данными элементы массива TBBUTTON, которые определяют конкретные кнопки. После того, как панель инструментов создана, можно определить дополнительные кнопки путем посылки сообщения TB_ADDBUTTONS панели инструментов или вставить кнопки между существующими кнопками путем посылки сообщения TB_INSERTBUTTON.

TBBUTTON определяется в файле COMMCTRL.H следующим образом:

typedef struct _TBBUTTON { int iBitmap;

int idCommand;

BYTE fsState;

BYTE fsStyle;

BYTE bReserved[2];

DWORD dwData;

int iString;

} TBBUTTON;

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

Поле iBitmap структуры TBBUTTON — это индекс (относительно нуля) изображения кнопки. При использовании битовых образов из библиотеки элементов управления общего пользования необходимо использовать константы STD_ и VIEW_ как значения в этом поле. В данной панели инструментов определяются собственные индексы изображений, в зависимости от порядка их включения в панель инструментов.

Поле idCommand структуры TBBUTTON — идентификатор команды, соответствующей кнопке. При нажатии кнопки она посылает сообщение WM_COMMAND с параметром wParam равным idCommand.

Поля fsState и fsStyle определяют начальное состояние и стиль кнопки. Последний не изменяется в течение всей жизни кнопки. Состояние кнопки устанавливается при ее создании и может быть изменено действиями пользователя или путем посылки сообщений панели инструментов. Ниже приведены пять стилей кнопок:

Стиль кнопки Описание TBSTYLE_BUTTON Кнопка ведет себя как стандартная кнопка (pushbutton).

Кнопка может быть нажата, но не может оставаться в нажатом состоянии.

TBSTYLE_SEP Разделитель для создания пространства между кнопками или для резервирования места для дочерних элементов управления (таких как комбинированный список).

TBSTYLE_CHECK Кнопка ведет себя как флажок (check box). Каждый щелчок мыши изменяет состояние кнопки (нажата/отжата).

TBSTYLE_GROUP Кнопка является членом группы кнопок типа переключателей (radio buttons). Кнопка остается нажатой до тех пор, пока не будет нажата другая кнопка из этой группы.

TBSTYLE_CHECKGROUP Объединяет свойства стилей TBSTYLE_CHECK и TBSTYLE_GROUP.

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

Ниже приведены шесть состояний кнопок:

Состояния кнопок Описание TBSTATE_CHECKED Кнопка стиля флажок находится в нажатом состоянии TBSTATE_PRESSED Кнопка любого стиля находится в нажатом состоянии TBSTATE_ENABLED Кнопка доступна (может реагировать на действия мышью) TBSTATE_HIDDEN Скрытая кнопка. (Такая кнопка не отображается и ее место занимают другие кнопки.) TBSTATE_INDETERMINATE Кнопка находится в неопределенном состоянии, отображается серым цветом, может быть нажата.

TBSTATE_WRAP Кнопки панели инструментов стиля TBSTYLE_WRAPABLE, находящиеся после кнопки, имеющей это состояние, отображаются в новой строке.

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

посылая сообщение TB_ENABLEBUTTON можно сделать кнопку доступной или недоступной.

Поле dwData структуры TBBUTTON — это необязательное 4-х байтовое поле, предназначенное для использования. Например, вы можете сохранить в этом поле указатель на данные, специфические для этой кнопки.

Возможно только однократная установка значения этого поля при создании кнопки. Вы можете получить значение из поля dwData путем посылки сообщения TB_GETBUTTON.

Поле iString структуры TBBUTTON — это индекс (относительно нуля) текстовой метки кнопки. Возможно добавление строк в список текстовых строк панели инструментов путем посылки сообщения TB_ADDSTRING.

Пример создания панели инструментов Ниже приведен пример создания панели инструментов с маленькими (16х16) изображениями кнопок, взятыми из стандартного битового образа, поддерживаемого библиотекой элементов управления общего пользования:

HWND hwndToolBar = CreateToolbarEx( hwndParent, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | CCS_TOP | TBSTYLE_TOOLTIPS, 1, 0, HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tbb, 5, 0, 0, 0, 0, sizeof(TBBUTTON) );

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

Ниже приведен массив TBBUTTON, необходимый для определения атрибутов конкретных кнопок панели инструментов:

TBBUTTON tbb[] = { STD_FILENEW, 1, TBSTATE_ENABLED, TBSTYLE_BUTTON,0, 0, 0, 0, STD_FILEOPEN, 2, TBSTATE_ENABLED, TBSTYLE_BUTTON,0, 0, 0, 0, STD_FILESAVE, 3, TBSTATE_ENABLED, TBSTYLE_BUTTON,0, 0, 0, 0, STD_PRINT, 4, TBSTATE_ENABLED, TBSTYLE_BUTTON,0, 0, 0, 0, STD_PRINTPRE, 5, TBSTATE_ENABLED, TBSTYLE_BUTTON,0, 0, 0, };

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

Ниже приведен код, который выполняет действия, аналогичные приведенному выше вызову функции CreateToolbarEx.

HWND hwndToolBar = CreateWindow( TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | CCS_TOP | TBSTYLE_TOOLTIPS, 0, 0, 0, 0, hwndParent, (HMENU) 1, hInst, );

// устанавливаем версию как размер структуры TBBUTTON ToolBar_ButtonStructSize( hwndToolBar );

ToolBar_AddBitmap( hwndToolBar, 1, &tbbitmap );

// создаем кнопки ToolBar_AddButtons( hwndToolBar, 5, tbb );

Требуется дополнительное определение данных типа структуры TBADDBITMAP, которые задают описатель экземпляра и идентификатор ресурса битового образа панели инструментов:

TBADDBITMAP tbbitmap = { HINST_COMMCTRL, IDB_STD_SMALL_COLOR };

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

Перемещение и изменение размеров панели инструментов Свойства панели инструментов автоматического изменения размеров и автоматического размещения начинают работать, когда панели инструментов посылается сообщение TB_AUTOSIZE. Обычно, это сообщение посылается тогда, когда родительское окно панели инструментов изменяет размер, т. е. получает сообщение WM_SIZE:

SendMessage( hwndToolBar, TB_AUTOSIZE, 0, 0L );

Вместо этого возможно использование макроса, определенного в файле COMCTHLP.H на прилагающемся компакт-диске:

ToolBar_AutoSize( hwndToolBar );

Реакция панели инструментов на это сообщение определяется флагами ее стиля. Например, панель инструментов стиля CCS_TOP или ССS_BOTTOM устанавливает свое местоположение и размеры. С другой стороны, панель инструментов стиля CCS_NORESIZE игнорирует это сообщение, и требует явной установки ее местоположения и размеров.

Поддержка элемента управления подсказка Элемент управления подсказка — это маленькое окно, содержащее текст подсказки. Обычно, подсказка активизируется, когда курсор мыши оказывается в специфической области кнопки панели инструментов. Однако существует возможность активизировать ее в любое время. Элемент управления подсказка хранит список "горячих" (hot) областей, которые в контексте окон подсказки называются инструментами (tool), и которые могут быть или прямоугольными областями в окне, или окнами целиком. Окна подсказки могут появляться у инструментов, расположенных в окне любого типа, хотя мы рассмотрим их в связи с панелью инструментов.

Панели инструментов, построенные с флагом TBSTYLE_TOOLTIPS, имеют элемент управления подсказка.

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

Когда подсказка становится активной, она делает запрос текста для отображения путем посылки сообщения WM_NOTIFY с кодом уведомления TTN_NEEDTEXT. Панель инструментов передает это сообщение своему родительскому окну, которое реагирует заполнением структуры TOOLTIPTEXT. Ниже приведен пример того, как необходимо обрабатывать этот запрос в случае, если панель инструментов состоит из трех кнопок и комбинированного списка:

case WM_NOTIFY:

{ LPNMHDR pnmh =(LPNMHDR) lParam;

LPSTR pReply;

// получить текст подсказки if(pnmh->code == TTN_NEEDTEXT) { LPTOOLTIPTEXT lpttt =(LPTOOLTIPTEXT) lParam;

switch( lpttt->hdr.idFrom ) { case 0:

pReply = "First Button";

break;

case 1:

pReply = "Second Button";

break;

case 2:

pReply = "Third Button";

break;

default:

if((lpttt->uFlags & TTF_IDISHWND) && (lpttt->hdr.idFrom==(UINT)hwndCombo)) pReply = "Combo box";

else pReply = "Unknown";

} lstrcpy(lpttt->szText, pReply);

} } Одно странное свойство этого фрагмента кода состоит в том, что параметр lParam сообщения WM_NOTIFY преобразуется к двум различным типам указателя: к указателю на NMHDR и к указателю на TOOLTIPTEXT. Это объясняется тем фактом, что все сообщения WM_NOTIFY передают структуру типа NMHDR, содержащую детали, относящиеся к каждому типу элемента управления общего пользования. Структура типа TOOLTIPTEXT содержит структуру типа NMHDR как первый член данных.

В файле WINUSER.H структура NMHDR определена так:

typedef struct tagNMHDR { HWND hwndFrom;

UINT idFrom;

UINT code;

} NMHDR;

typedef NMHDR FAR *LPNMHDR;

Третий член этой структуры code содержит код уведомления, т. е. причины, по которой было послано сообщение WM_NOTIFY. Он может принимать значение одного из основных кодов уведомления (таких как NM_CLICK или NM_SETFOCUS) или специфичного кода уведомления для конкретного класса элемента управления (такого как TTN_NEEDTEXT в данном примере). Поле hwndFrom идентифицирует окно, пославшее сообщение, idFrom — или идентификатор окна, или идентификатор конкретного элемента (такого как кнопка панели инструментов) от имени которого было послано сообщение.

Когда подсказка посылает уведомление TTN_NEEDTEXT, она передает указатель на структуру данных, уникальную для этого кода уведомления: TOOLTIPTEXT. Эта структура определена в файле COMMCTRL.H следующим образом:

typedef struct tagTOOLTIPTEXTA { NMHDR hdr;

LPSTR lpszText;

char szText[80];

HINSTANCE hInst;

UINT uFlags;

} TOOLTIPTEXTA, FAR *LPTOOLTIPTEXTA;

Первый член структуры, hdr, содержит данные типа NMHDR, которые должны сопровождать любое сообщение WM_NOTIFY.

При получении сообщения TTN_NEEDTEXT первая задача — проверить, какая кнопка (или окно) была выбрана.

Это осуществляется путем проверки поля idFrom структуры NMHDR, которое может быть либо индексом кнопки, если мышь над кнопкой, либо описателем окна, если мышь над дочерним окном панели инструментов. Поле uFlags содержит флаг TTF_IDISHWND для индикации того, что это описатель окна, а не индекс кнопки.

При получении уведомления TTN_NEEDTEXT существует три пути для передачи текста в подсказку: путем передачи указателя на строку в поле lpszText, путем копирования строки в буфер szText или путем задания идентификатора строки-ресурса. Так же как и при работе с другими ресурсами, строка-ресурс задается с помощью описателя экземпляра и уникального идентификатора. Описатель экземпляра копируется в поле hinst, а идентификатор — в поле lpszText, используя макрос MAKEINTRESOURCE:

lpttt->hinst = hInstance;

lpttt->lpszText = MAKEINTRESOURCE(100);

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

Поскольку основы создания дочерних окон рассмотрены в главе 8, здесь мы остановимся на некоторых подробностях: резервирование места в панели инструментов для дочернего окна, изменение размеров панели инструментов, поддержка окон подсказки.

Резервирование места для дочернего окна на панели инструментов В книге "Programming the Windows 95 User Interface" Nancy Cluts предлагает включать разделители (кнопки стиля TBSTYLE_SEP) для сохранения пустого пространства при создании дочернего окна на панели инструментов. Вам предстоит поэкспериментировать, чтобы определить правильное число разделителей, а пока программа GADGETS, приведенная далее в этой главе, использует 20 разделителей для резервирования места для комбинированного списка.

Присутствие разделителей упрощает расчет координат для окна, которые необходимо задать при вызове функции CreateWindow. Посылается сообщение TB_GETITEMRECT для получения координат в пикселях любого элемента панели инструментов — кнопки или разделителя, и возвращается четыре координаты прямоугольника указанного элемента. Ниже показано, каким образом программа GADGETS вычисляет размеры пространства, доступного для комбинированного списка, используя макрос сообщения из файла COMMCTRL.H (находится на прилагающемся компакт-диске):

RECT r;

int x, y, cx, cy;

// Вычисляем координаты для комбинированного списка ToolBar_GetItemRect( hwndTB, 0, &r );

x = r.left;

y = r.top;

ToolBar_GetItemRect( hwndTB, 18, &r );

cx = r.right — x + 1;

Левый верхний угол элемента управления устанавливается в точку с координатами (x, y), в соответствии с размерами кнопок панели инструментов. Поскольку программа GADGETS использовала 20 разделителей на своей панели инструментов, запрос положения 19-го (или 18 при отсчете с нуля) разделителя, оставляет один разделитель между правым краем комбинированного списка и первой кнопкой.

Другим значением, необходимым для помещения дочернего окна на панель инструментов, является высота, которая для большинства элементов управления будет равна высоте кнопки (r.bottom — r.top + 1), полученной из сообщения TB_GETITEMRECT. Но комбинированный список ведет себя несколько иначе, чем другие типы окон, поскольку его часть редактирования (или статическая) изменяет размер самостоятельно с тем, чтобы прийти в соответствие шрифту, и высота, передаваемая в функцию CreateWindow, есть возможная высота для ниспадающего окна. Установка значения, достаточного для нескольких строк, хороша тогда, когда вы используете для расчета данные о шрифте, получаемые от функции GetTextMetrics. Программа GADGETS этого не делает и устанавливает это значение жестко:

cy = 100;

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

Очевидным выбором родительского окна для окон на панели инструментов является само окно панели инструментов, но это может привести к странным проявлениям. В частности, предопределенные элементы управления посылают уведомляющие сообщения (в виде сообщений WM_COMMAND) своему родителю, что может иметь неожиданный результат. Например, программа GADGETS постоянно сбивается в Windows 95, когда ее окно комбинированного списка панели инструментов создается с окном панели инструментов в роли родительского окна, а идентификатор окна комбинированного списка равен 101. (Заметим, что эта комбинация не вызывает никаких проблем в Windows NT 3.51. Это связано с отличающимися в этих операционных системах версиями библиотеки COMCTL32.DLL.) Изменение управляющего идентификатора — кажущееся решение этой проблемы, поскольку такой путь оставляет шанс (хоть и незначительный) на то, что в будущих версиях библиотеки элементов управления общего пользования будет использоваться идентификатор, который используем мы, и тогда указанная проблема возникнет вновь. И все же, панель инструментов должна быть родительским окном для того окна, которое хочет располагаться на ее поверхности.

Решение этого противоречия состоит в задании другого окна в качестве родительского в вызове функции CreateWindow — предпочтительнее одного из тех, которые имеют вашу собственную оконную процедуру. После того, как дочернее окно будет создано, оно помещается на панель инструментов путем вызова функции SetParent.

Элементы управления диалогового окна лояльны к этому и посылают свои уведомляющие сообщения WM_COMMAND их первоначальному родительскому окну, что позволяет вам обрабатывать уведомляющие сообщения по своему усмотрению. Таким образом, уведомляющие сообщения посылаются одному окну, а дочернее окно физически располагается на другом окне — панели инструментов. Программа GADGETS делает эти два шага следующим образом:

hwndCombo = CreateWindow( "combobox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, x, y, cx, cy, hwndParent, (HMENU) 100, hInst, );

// Установление панели инструментов как родительского окна для комбинированного списка // SetParent( hwndCombo, hwndTB );

Изменение размеров панели инструментов с дочерними окнами Другая сложность, возникающая при использовании дочерних окон на панели инструментов состоит в том, что необходимо быть уверенным, что размеры самой панели инструментов достаточны для размещения дочерних окон. Между прочим, эта проблема актуальна для оболочек типа старой Windows, такой как Windows NT 3.51, а не для новейшей оболочки Windows 95. Но поскольку некоторые из пользователей вашей программы могут работать на указанной версии Windows NT, следует тестировать вашу программу в обеих средах.

Простейшее решение, для всех элементов управления кроме комбинированных списков, состоит в изменении размеров окна самим окном. Работа с комбинированным списком более трудоемка, поскольку он изменяет свои размеры в зависимости от текущего шрифта. Поэтому, вы можете косвенно воздействовать на размеры комбинированного списка, устанавливая его шрифт. Это делается путем создания шрифта необходимого размера и передачи его в комбинированный список путем посылки сообщения WM_SETFONT. Пока элемент управления короче, чем кнопки панели инструментов, обработка сообщения TB_AUTOSIZE будет выполнять всю хлопотливую работу по приведению размеров панели инструментов в соответствие с изменяющимися размерами ее родительского окна. (См. предшествующее обсуждение размещения и изменения размеров панели инструментов.) Если вы не можете заставить дочернее окно быть меньше, необходимо сделать панель инструментов больше.

Программа GADGETS демонстрирует этот подход и является основой для дальнейшего обсуждения.

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

Поэтому, перемещение и установка местоположения панели инструментов целиком в вашей власти. Когда родительское окно получает сообщение WM_SIZE, вам следует рассчитать необходимые координаты местоположения (x, y) и размеры окна, а затем произвести необходимые изменения путем вызова функции типа MoveWindow.

Для приложений с простой панелью инструментов в верхней части родительского окна местоположение определяется просто — (0, 0 ). Ширина также определяется просто, поскольку сообщение WM_SIZE родительскому окну содержит в младшем слове параметра lParam ширину окна. Для изменения размеров панели инструментов в ответ на сообщение WM_SIZE необходим примерно такой код:

case WM_SIZE:

{ int cx = LOWORD(lParam);

MoveWindow(hwndToolBar, 0, 0, cx, cyToolBar, TRUE);

[другие строки программы] return 0;

} Остается пока непонятным, как определить высоту панели инструментов (cyToolBar). В программе GADGETS проблема решается в два этапа: сначала делается запрос определения размера комбинированного списка, затем запрос определения размера элемента панели инструментов, к которому добавляется дополнительная величина, равная 5 (величина поля, строго определенная в панели инструментов). Высота панели инструментов и есть максимум из этих двух значений:

// вычислить высоту панели инструментов GetWindowRect(hwndCombo, &r);

cyToolBar = r.bottom — r.top + 1;

cyToolBar += y;

cyToolBar +=(2 * GetSystemMetrics(SM_CYBORDER));

ToolBar_GetItemRect(hwndTB, 0, &r);

cyToolBar = max(cyToolBar, r.bottom + 5);

Поддержка окон подсказки для дочерних окон панели инструментов Кроме обработки уведомления TTN_NEEDTEXT создание работоспособного окна подсказки для кнопок панели инструментов — дело достаточно простое. Для дочерних окон в панели инструментов требуется еще одно дополнительное усилие: добавление дочернего окна в список инструментов, создаваемый элементом управления подсказка.

Добавление дочернего окна в список инструментов, создаваемый элементом управления подсказка, требует посылки сообщения TTM_ADDTOOL окну подсказки. Но для начала необходимо получить описатель окна подсказки. Это делается путем посылки панели инструментов сообщения TB_GETTOOLTIP:

hwndTT = ToolBar_GetToolTips(hwndToolBar);

Сообщение TTM_ADDTOOL требует указатель на структуру типа TOOLINFO, которая описывает инструмент элемента управления подсказка. Структура TOOLINFO определена в файле COMMCTRL.H как показано ниже:

typedef struct tagTOOLINFOA { UINT cbSize;

UINT uFlags;

HWND hwnd;

UINT uId;

RECT rect;

HINSTANCE hinst;

LPSTR lpszText;

} TOOLINFOA, NEAR *PTOOLINFOA, FAR *LPTOOLINFOA;

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

Поле uFlags позволяет иметь некоторую гибкость при задании инструмента. Первый флаг, TTF_IDISHWND, требуется тогда, когда в качестве инструмента передается описатель дочернего окна. Он показывает, что в поле uId находится описатель окна. Другой флаг, TTF_CENTERTIP, заставляет окно подсказки выводиться в центре по горизонтали под инструментом, а не как по умолчанию — ниже и справа относительно курсора мыши. Третий флаг, TTF_SUBCLASS, требует, чтобы элемент управления подсказка создал новую оконную процедуру для отслеживания потока сообщений. Без этого флага вам пришлось бы создавать другой механизм для передачи подробностей сообщений мыши элементу управления подсказка.

Третье поле, hwnd, идентифицирует окно, содержащее инструмент. Конечно, в данном случае это окно панели инструментов.

Поле uId содержит произвольный идентификатор, который создатель инструмента (например, панель инструментов) присваивает каждому инструменту. Например, панель инструментов устанавливает каждой своей кнопке индекс относительно нуля и передает этот индекс в окно подсказки, когда добавляет каждую кнопку в список подсказки. Когда этот идентификатор содержит описатель окна, как это бывает, если целое дочернее окно становится инструментом, то флаг TTF_IDISHWND в поле uFlags уведомляет об этом подсказку.

Местоположение инструмента в его родительском окне задается структурой rect, что позволяет элементу управления подсказка осуществлять контроль положения указателя мыши для каждой конкретной кнопки на панели инструментов. В случае, если окно целиком является инструментом, т. е. если указан флаг TTF_IDISHWND, то это поле игнорируется.

Текст, который выводится в качестве подсказки на дисплей, определяется комбинацией значений полей hinst и lpszText, задающих текстовую строку как ресурс. В качестве альтернативы, возможно указать, что необходим запрос в уведомляющем сообщении TTN_NEEDTEXT. Для этого надо указать в поле lpszText значение LPSTR_TEXTCALLBACK.

Ниже показано, как программа GADGETS добавляет поддержку окна подсказки для комбинированного списка, находящегося в панели инструментов:

TOOLINFO ti;

ZeroMemory(&ti, sizeof(TOOLINFO));

ti.cbSize = sizeof(TOOLINFO);

ti.uFlags = TTF_IDISHWND | TTF_CENTERTIP | TTF_SUBCLASS;

ti.hwnd = hwndToolBar;

ti.uId =(UINT)(HWND) hwndComboBox;

ti.lpszText = LPSTR_TEXTCALLBACK;

bSuccess = ToolTip_AddTool(hwndTT, &ti);

Ценность комбинированных списков в том, что они состоят из комбинации двух или более элементов управления:

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

// Добавление окна подсказки для окна редактирования комбинированного списка hwndEdit = GetWindow(hwndComboBox, GW_CHILD);

ti.cbSize = sizeof(TOOLINFO);

ti.uFlags = TTF_IDISHWND | TTF_CENTERTIP | TTF_SUBCLASS;

ti.hwnd = hwndToolBar;

ti.uId =(UINT)(HWND) hwndEdit;

ti.lpszText = LPSTR_TEXTCALLBACK;

bSuccess = ToolTip_AddTool(hwndTT, &ti);

Pages:     | 1 |   ...   | 8 | 9 || 11 | 12 |



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

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