
- •Глава 11 Окна диалога
- •Модальные окна диалога
- •Создание окна диалога About
- •Шаблон окна диалога
- •Диалоговая процедура
- •Вызов окна диалога
- •Дополнительная информация о стиле окна диалога
- •Дополнительная информация об определении дочерних окон элементов управления
- •Более сложное окно диалога
- •Работа с дочерними элементами управления окна диалога
- •Кнопки ok и Cancel
- •Позиции табуляции и группы
- •Рисование в окне диалога
- •Использование с окном диалога других функций
- •Определение собственных окон управления
- •Окна сообщений
- •Информация во всплывающих окнах
- •Немодальные окна диалога
- •Различия между модальными и немодальными окнами диалога
- •Новая программа colors
- •Программа hexcalc: обычное окно или окно диалога?
- •Творческое использование идентификаторов дочерних окон элементов управления
- •Диалоговые окна общего пользования
- •Модернизированная программа poppad
- •Изменение шрифта
- •Поиск и замена
- •Программа для Windows, содержащая всего один вызов функции
Программа hexcalc: обычное окно или окно диалога?
Возможно, вершиной программистской лени является программа 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, 1996
----------------------------------------*/
#include <windows.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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 <windows.h>
HexCalc ICON hexcalc.ico
HexCalc DIALOG 32768, 0, 102, 122
STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
CLASS "HexCalc"
CAPTION "Hex Calculator"
{
PUSHBUTTON "D", 68, 8, 24, 14, 14
PUSHBUTTON "A", 65, 8, 40, 14, 14
PUSHBUTTON "7", 55, 8, 56, 14, 14
PUSHBUTTON "4", 52, 8, 72, 14, 14
PUSHBUTTON "1", 49, 8, 88, 14, 14
PUSHBUTTON "0", 48, 8, 104, 14, 14
PUSHBUTTON "0", 27, 26, 4, 50, 14
PUSHBUTTON "E", 69, 26, 24, 14, 14
PUSHBUTTON "B", 66, 26, 40, 14, 14
PUSHBUTTON "8", 56, 26, 56, 14, 14
PUSHBUTTON "5", 53, 26, 72, 14, 14
PUSHBUTTON "2", 50, 26, 88, 14, 14
PUSHBUTTON "Back", 8, 26, 104, 32, 14
PUSHBUTTON "C", 67, 44, 40, 14, 14
PUSHBUTTON "F", 70, 44, 24, 14, 14
PUSHBUTTON "9", 57, 44, 56, 14, 14
PUSHBUTTON "6", 54, 44, 72, 14, 14
PUSHBUTTON "3", 51, 44, 88, 14, 14
PUSHBUTTON "+", 43, 62, 24, 14, 14
PUSHBUTTON "-", 45, 62, 40, 14, 14
PUSHBUTTON "*", 42, 62, 56, 14, 14
PUSHBUTTON "/", 47, 62, 72, 14, 14
PUSHBUTTON "%", 37, 62, 88, 14, 14
PUSHBUTTON "Equals", 61, 62, 104, 32, 14
PUSHBUTTON "&&", 38, 80, 24, 14, 14
PUSHBUTTON "|", 124, 80, 40, 14, 14
PUSHBUTTON "^", 94, 80, 56, 14, 14
PUSHBUTTON "<", 60, 80, 72, 14, 14
PUSHBUTTON ">", 62, 80, 88, 14, 14
}
HEXCALC.ICO
Рис. 11.9. Программа HEXCALC
Рис. 11.10. Окно программы HEXCALC
Программа HEXCALC — это обычный калькулятор с нефиксированной записью, использующий обозначение языка C для записи операций. Он работает с беззнаковыми 32-разрядными целыми и может выполнять сложение, вычитание, умножение, деление и определять остаток от деления; осуществлять поразрядные операции AND, OR и исключающее OR; сдвигать числа влево или вправо на один разряд. Деление на 0 приводит к результату FFFFFFFF.
Для работы с калькулятором в программе HEXCALC можно использовать как мышь, так и клавиатуру. Начинать надо с ввода первого числа, щелкнув мышью над ним или набрав его на клавиатуре (до восьми шестнадцатеричных цифр), затем вводится знак операции и второе число. Вывести результат можно либо щелкнув мышью на кнопке Equals, либо нажав клавиши <Equals> или <Enter>. Для исправления ввода используется кнопка Back, либо клавиши <Backspace> или "Стрелка влево". Щелчок в окошке с результатом или нажатие клавиши <Esc> полностью стирает текущий результат.
Необычность программы HEXCALC состоит в том, что выводимое на экран окно кажется гибридом обычного перекрывающегося окна и немодального окна диалога. С одной стороны, все сообщения в программе HEXCALC обрабатываются в функции WndProc, которая, как кажется, является обычной оконной процедурой. Функция возвращает длинное целое число и обрабатывает сообщение WM_DESTROY и, как обычная оконная процедура, вызывает DefWindowProc. С другой стороны, окно создается в функции WinMain с помощью вызова функции CreateDialog, использующей шаблон окна диалога из файла HEXCALC.RC. Итак, что же представляет из себя программа HEXCALC, обычное перекрывающееся окно или немодальное окно диалога?
Ответ прост, он состоит в том, что окно диалога — это тоже окно. Обычно Windows использует свою собственную внутреннюю оконную процедуру для обработки сообщений всплывающего окна диалога. Затем Windows передает эти сообщения процедуре окна диалога внутри программы, создавшей окно диалога. В программе HEXCALC мы заставляем Windows для образования всплывающего окна применять шаблон окна диалога, но сообщения для этого окна мы обрабатываем сами.
Внимательное изучение файла HEXCALC.RC позволит обнаружить, как это делается. Начало шаблона окна диалога выглядит так:
HexCalc DIALOG 32768, 0, 102, 122
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. Это превосходный способ создания такого окна, в котором должно содержаться множество дочерних окон.