Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ch-08.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
645.12 Кб
Скачать

Кнопки, определяемые пользователем

Если вы хотите полностью управлять внешним обликом кнопки, но не хотите связываться с логикой обработки клавиатуры и мыши, вы можете создать кнопку стиля 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, выбранные в контекст устройства, должны быть удалены из контекста. Кроме того, будьте внимательны, не рисуйте вне прямоугольника, задающего границы кнопки.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]