
- •Глава 8 Дочерние окна управления
- •Класс кнопок
- •Создание дочерних окон
- •Сообщения дочерних окон родительскому окну
- •Сообщения родительского окна дочерним окнам
- •Нажимаемые кнопки
- •Переключатели
- •Окна группы
- •Изменение текста кнопки
- •Видимые и доступные кнопки
- •Кнопки и фокус ввода
- •Дочерние окна управления и цвета
- •Системные цвета
- •Цвета кнопок
- •Сообщение wm_ctlcolorbtn
- •Кнопки, определяемые пользователем
- •Класс статических дочерних окон
- •Класс полос прокрутки
- •Программа colors1
- •Интерфейс клавиатуры, поддерживаемый автоматически
- •Введение новой оконной процедуры
- •Закрашивание фона
- •Окрашивание полос прокрутки и статического текста
- •Класс редактирования
- •Стили класса редактирования
- •Коды уведомления управляющих окон редактирования
- •Использование управляющих окон редактирования
- •Сообщения управляющему окну редактирования
- •Класс окна списка
- •Стили окна списка
- •Добавление строк в окно списка
- •Выбор и извлечение элементов списка
- •Получение сообщений от окон списка
- •Простое приложение, использующее окно списка
- •Список файлов
- •Использование атрибутов файлов
- •Упорядочивание списков файлов
- •Утилита Head для Windows
Кнопки, определяемые пользователем
Если вы хотите полностью управлять внешним обликом кнопки, но не хотите связываться с логикой обработки клавиатуры и мыши, вы можете создать кнопку стиля BS_OWNERDRAW, как показано в программе OWNERDRW, приведенной на рис. 8.3.
OWNERDRW.MAK
#------------------------
# OWNERDRW.MAK make file
#------------------------
ownerdrw.exe : ownerdrw.obj
$(LINKER) $(GUIFLAGS) -OUT:ownerdrw.exe ownerdrw.obj $(GUILIBS)
ownerdrw.obj : ownerdrw.c
$(CC) $(CFLAGS) ownerdrw.c
OWNERDRW.C
/*----------------------------------------------
OWNERDRW.C -- Owner-Draw Button Demo Program
(c) Charles Petzold, 1996
----------------------------------------------*/
#include <windows.h>
#define IDC_SMALLER 1
#define IDC_LARGER 2
#define BTN_WIDTH (8 * cxChar)
#define BTN_HEIGHT (4 * cyChar)
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HINSTANCE hInst ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "OwnerDrw" ;
MSG msg ;
HWND hwnd ;
WNDCLASSEX wndclass ;
hInst = hInstance ;
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 (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
RegisterClassEx (&wndclass) ;
hwnd = CreateWindow (szAppName, "Owner-Draw Button Demo",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
void Triangle (HDC hdc, POINT pt[])
{
SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
Polygon (hdc, pt, 3) ;
SelectObject (hdc, GetStockObject (WHITE_BRUSH)) ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hwndSmaller, hwndLarger ;
static int cxClient, cyClient, cxChar, cyChar ;
int cx, cy ;
LPDRAWITEMSTRUCT pdis ;
POINT pt[3] ;
RECT rc ;
switch (iMsg)
{
case WM_CREATE :
cxChar = LOWORD (GetDialogBaseUnits ()) ;
cyChar = HIWORD (GetDialogBaseUnits ()) ;
// Create the owner-draw pushbuttons
hwndSmaller = CreateWindow ("button", "",
WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
0, 0, BTN_WIDTH, BTN_HEIGHT,
hwnd, (HMENU) IDC_SMALLER, hInst, NULL) ;
hwndLarger = CreateWindow ("button", "",
WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
0, 0, BTN_WIDTH, BTN_HEIGHT,
hwnd, (HMENU) IDC_LARGER, hInst, NULL) ;
return 0 ;
case WM_SIZE :
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
// Move the buttons to the new center
MoveWindow (hwndSmaller, cxClient / 2 - 3 * BTN_WIDTH / 2,
cyClient / 2 - BTN_HEIGHT / 2,
BTN_WIDTH, BTN_HEIGHT, TRUE) ;
MoveWindow (hwndLarger, cxClient / 2 + BTN_WIDTH / 2,
cyClient / 2 - BTN_HEIGHT / 2,
BTN_WIDTH, BTN_HEIGHT, TRUE) ;
return 0 ;
case WM_COMMAND :
GetWindowRect (hwnd, &rc) ;
// Make the window 10% smaller or larger
switch (wParam)
{
case IDC_SMALLER :
rc.left += cxClient / 20 ;
rc.right -= cxClient / 20 ;
rc.top += cyClient / 20 ;
rc.bottom -= cyClient / 20 ;
break ;
case IDC_LARGER :
rc.left -= cxClient / 20 ;
rc.right += cxClient / 20 ;
rc.top -= cyClient / 20 ;
rc.bottom += cyClient / 20 ;
break ;
}
MoveWindow (hwnd, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, TRUE) ;
return 0 ;
case WM_DRAWITEM :
pdis = (LPDRAWITEMSTRUCT) lParam ;
// Fill area with white and frame it black
FillRect (pdis->hDC, &pdis->rcItem,
(HBRUSH) GetStockObject (WHITE_BRUSH)) ;
FrameRect (pdis->hDC, &pdis->rcItem,
(HBRUSH) GetStockObject (BLACK_BRUSH)) ;
// Draw inward and outward black triangles
cx = pdis->rcItem.right - pdis->rcItem.left ;
cy = pdis->rcItem.bottom - pdis->rcItem.top ;
switch (pdis->CtlID)
{
case IDC_SMALLER :
pt[0].x = 3 * cx / 8 ; pt[0].y = 1 * cy / 8 ;
pt[1].x = 5 * cx / 8 ; pt[1].y = 1 * cy / 8 ;
pt[2].x = 4 * cx / 8 ; pt[2].y = 3 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 7 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
pt[1].x = 7 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
pt[2].x = 5 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 5 * cx / 8 ; pt[0].y = 7 * cy / 8 ;
pt[1].x = 3 * cx / 8 ; pt[1].y = 7 * cy / 8 ;
pt[2].x = 4 * cx / 8 ; pt[2].y = 5 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 1 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
pt[1].x = 1 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
pt[2].x = 3 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
break ;
case IDC_LARGER :
pt[0].x = 5 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
pt[1].x = 3 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
pt[2].x = 4 * cx / 8 ; pt[2].y = 1 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 5 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
pt[1].x = 5 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
pt[2].x = 7 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 3 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
pt[1].x = 5 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
pt[2].x = 4 * cx / 8 ; pt[2].y = 7 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 3 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
pt[1].x = 3 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
pt[2].x = 1 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
break ;
}
// Invert the rectangle if the button is selected
if (pdis->itemState & ODS_SELECTED)
InvertRect (pdis->hDC, &pdis->rcItem) ;
// Draw a focus rectangle if the button has the focus
if (pdis->itemState & ODS_FOCUS)
{
pdis->rcItem.left += cx / 16 ;
pdis->rcItem.top += cy / 16 ;
pdis->rcItem.right -= cx / 16 ;
pdis->rcItem.bottom -= cy / 16 ;
DrawFocusRect (pdis->hDC, &pdis->rcItem) ;
}
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
Рис. 8.3. Программа OWNERDRW
В этой программе создаются две кнопки в центре рабочей области окна программы, как показано на рис. 8.4. На левой кнопке находятся четыре треугольника, направленные в центр кнопки. Щелчок на этой кнопке вызывает уменьшение размеров окна на 10%. На правой кнопке находятся четыре треугольника, направленные от центра кнопки к ее сторонам, щелчок на этой кнопке вызывает увеличение размеров окна на 10%.
Рис. 8.4. Вид экрана программы OWNERDRW
Большинство программ, в которых для рисования собственных кнопок используется стиль кнопки BS_OWNERDRAW, часто для изображения применяют небольшие растровые образы кнопки. Что же касается программы OWNERDRW, то она просто рисует треугольники на поверхности кнопки.
При обработке сообщения WM_CREATE программа OWNERDRW с помощью вызова функции GetDialogBaseUnits получает среднюю высоту и ширину системного шрифта. Эта функция часто очень удобна для получения такой информации. Затем программа OWNERDRW создает две кнопки стиля BS_OWNERDRAW; кнопкам задается ширина, в восемь раз превышающая ширину системного шрифта, и высота, превышающая высоту системного шрифта в четыре раза. (При использовании для рисования кнопок, предопределенных в системе битовых образов, полезно знать, что эти размеры создают кнопки размером 64 на 64 пикселя на VGA.) Но кнопки еще не спозиционированы. При обработке сообщения WM_SIZE, программа OWNERDRW с помощью функции MoveWindow позиционирует кнопки в центр рабочей области.
Щелчок на кнопках заставляет их генерировать сообщение WM_COMMAND. Для обработки сообщения WM_COMMAND программа вызывает функцию GetWindowRect, чтобы сохранить положение и размер всего окна (а не только рабочей области) в структуре RECT (прямоугольник). Это положение определяется относительно экрана. Затем программа OWNERDRW модифицирует поля этой структуры в соответствии с тем, на левой или на правой кнопке был щелчок. Затем программа с помощью функции MoveWindow меняет положение и размер окна, что генерирует другое сообщение WM_SIZE, и кнопки перемещаются в центр рабочей области.
Если бы это было все, что делает программа, она была бы полностью работоспособной, но кнопки были бы невидимы. Кнопки стиля BS_OWNERDRAW при необходимости перерисовки посылают своему родительскому окну сообщение WM_DRAWITEM. Это происходит при первоначальном создании кнопки, при ее нажатии или отпускании, при получении или потере фокуса ввода и во всех других случаях, когда требуется перерисовка.
При обработке сообщения WM_DRAWITEM параметр lParam этого сообщения является указателем на структуру типа DRAWITEMSTRUCT. Программа OWNERDRW хранит этот указатель в переменной pdis. Эта структура содержит информацию, необходимую программе для рисования кнопки. (Такая же структура используется для создания определяемых пользователем списков.) Полями структуры, которые важны для работы с кнопками, являются hDC (контекст устройства для кнопки), rcItem (структура RECT с размерами кнопки), CtlID (идентификатор окна управления) и itemState (которое показывает, нажата ли кнопка и имеет ли она фокус ввода).
Программа OWNERDRW начинает обработку сообщения WM_DRAWITEM с вызова функции FillRect для обновления поверхности кнопки белой кистью и с вызова функции FrameRect для рисования черной рамки вокруг кнопки. Затем, вызывая функцию Polygon, программа OWNERDRW рисует четыре черных треугольника. Это обычный случай.
Если кнопка в данный момент нажимается, то в поле itemState структуры DRAWITEMSTRUCT устанавливается бит. Вы можете выяснить, установлен ли этот бит, используя константу ODS_SELECTED. Если бит установлен, программа OWNERDRW, вызывая функцию InvertRect, меняет цвет кнопки на обратный. Если кнопка имеет фокус ввода, тогда будет установлен бит ODS_FOCUS поля itemState. В этом случае программа OWNERDRW с помощью вызова функции DrawFocusRect рисует точечный прямоугольник внутри кнопки точно вдоль ее сторон.
Предупреждение, связанное с использованием кнопок, определяемых пользователем: Windows получает для вас контекст устройства и включает его в виде поля в структуру DRAWITEMSTRUCT. После использования контекста устройства, оставляйте его в том же состоянии, в каком он находился до этого. Все объекты GDI, выбранные в контекст устройства, должны быть удалены из контекста. Кроме того, будьте внимательны, не рисуйте вне прямоугольника, задающего границы кнопки.