Операционная система Microsoft Windows 3.1 для программиста -том 2

         

Файл ownbut\ownbut.cpp


// ---------------------------------------- // Управление нестандартной кнопкой // ----------------------------------------

#define STRICT #include <windows.h>
#include <mem.h>
#include "ownbut.hpp"

// Прототипы функций BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawButton(LPDRAWITEMSTRUCT);
void DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap);

// Имя класса окна char const szClassName[] = "OwnButtonAppClass";

// Заголовок окна char const szWindowTitle[] = "Owner Draw Button Demo";

// Идентификаторы кнопок HWND hButton1, hButton2, hButton3, hButton4, hButton5;

// Идентификатор копии приложения HINSTANCE hInst;

// ===================================== // Функция WinMain // ===================================== #pragma argsused

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения

// Сохраняем идентификатор текущей копии приложения // в глобальной переменной hInst = hInstance;

// Инициализируем приложение if(!InitApp(hInstance)) return FALSE;

// После успешной инициализации приложения создаем // главное окно приложения hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL);
// указатель на дополнительные // параметры

// Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE;

// Рисуем главное окно ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Создаем пять кнопок

// Эту кнопку будет рисовать функция // родительского окна hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 20, 20, 180, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);


hButton2 = CreateWindow("button", "PUSH", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 60, 90, 30, hwnd, (HMENU) IDB_Button2, hInstance, NULL);

hButton3 = CreateWindow("button", "POP", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 100, 90, 30, hwnd, (HMENU) IDB_Button3, hInstance, NULL);

hButton4 = CreateWindow("button", "OFF", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 140, 90, 30, hwnd, (HMENU) IDB_Button4, hInstance, NULL);

hButton5 = CreateWindow("button", "ON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 180, 90, 30, hwnd, (HMENU) IDB_Button5, hInstance, NULL);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg);
} return msg.wParam; }

// ===================================== // Функция InitApp // Выполняет регистрацию класса окна // =====================================

BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна

memset(&wc, 0, sizeof(wc));

wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szClassName;

// Регистрация класса aWndClass = RegisterClass(&wc);

return (aWndClass != 0);
}

// ===================================== // Функция WndProc // =====================================

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { // Сообщение приходит, когда вы нажимаете // на одну из двух созданных кнопок case WM_COMMAND: { // Если нажата первая кнопка, выводим // сообщение if(wParam == IDB_Button1) { MessageBox(hwnd, "Нажата кнопка Button 1", "Message WM_COMMAND",MB_OK);
} else if(wParam == IDB_Button2) { SendMessage(hButton1, BM_SETSTATE, TRUE, 0L);
} else if(wParam == IDB_Button3) { SendMessage(hButton1, BM_SETSTATE, FALSE, 0L);
} else if(wParam == IDB_Button4) { EnableWindow(hButton1, FALSE);
} else if(wParam == IDB_Button5) { EnableWindow(hButton1, TRUE);
} return 0; }



// Это сообщение приходит при изменении состояния // дочернего окна органа управления, когда окно // нужно перерисовать case WM_DRAWITEM: { // Перерисовываем кнопку DrawButton( (LPDRAWITEMSTRUCT)lParam );
break; }

case WM_DESTROY: { PostQuitMessage(0);
return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam);
}

// ===================================== // Функция DrawButton // Перерисовывает кнопку // =====================================

void DrawButton(LPDRAWITEMSTRUCT lpInfo) { HBITMAP hbm; int ResourceID;

// Обрабатываем сообщение WM_DRAWITEM // только если оно поступило от кнопки if(lpInfo->
CtlType != ODT_BUTTON) return;

// Так как в приложении может быть несколько // кнопок, посылающих сообщение WM_DRAWITEM, // проверяем идентификатор кнопки switch (lpInfo->
CtlID) { case IDB_Button1: { // Загружаем идентификатор изображения // кнопки в нормальном (отжатом) состоянии ResourceID = IDR_BUTTONUP; break; } default: return; }

// Если кнопка выбрана, рисуем ее в нажатом // состоянии if (lpInfo->
itemState & ODS_SELECTED) { ResourceID = IDR_BUTTONDOWN; }

// если кнопка неактивна, загружаем идентификатор // изображения кнопки в неактивном состоянии else if (lpInfo->
itemState & ODS_DISABLED) { ResourceID = IDR_BUTTONGR; }

// Загружаем изображение кнопки из ресурсов приложения hbm = LoadBitmap(hInst, MAKEINTRESOURCE(ResourceID));

// При ошибке ничего не рисуем if(!hbm) return;

// Если кнопка выбрана и ее надо целиком // перерисовать, вызываем функцию DrawBitmap if((lpInfo->
itemAction & ODA_DRAWENTIRE) (lpInfo->
itemAction & ODA_SELECT)) { // Рисуем кнопку DrawBitmap(lpInfo->
hDC, (lpInfo->
rcItem).left, (lpInfo->
rcItem).top , hbm);
}

// Удаляем изображение кнопки, так как оно // нам больше не нужно DeleteObject(hbm);
}

В начале своей работы функция WinMain сохраняет идентификатор текущей копии приложения в глобальной переменной:

hInst = hInstance;

Затем она инициализирует главное окно приложения и создает пять кнопок, первая из которых имеет стиль BS_OWNERDRAW:



hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 20, 20, 180, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);

Функция главного окна приложения OWNBTN кроме сообщения WM_COMMAND дополнительно обрабатывает сообщение WM_DRAWITEM, в ответ на которое она вызывает функцию DrawButton, определенную в нашем приложении:

case WM_DRAWITEM: { DrawButton( (LPDRAWITEMSTRUCT)lParam );
break; }

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

Вначале эта функция проверяет тип органа управления, приславшего сообщение WM_DRAWITEM. Тип должен быть равен ODT_BUTTON.

Затем проверяется идентификатор кнопки. В нашем приложении есть только одна кнопка со стилем BS_OWNERDRAW, но их могло бы быть и несколько. В переменную ResourceID загружается идентификатор ресурса (изображения bitmap), соответствующего изображению кнопки в нормальном (отжатом) состоянии:

switch (lpInfo->
CtlID) { case IDB_Button1: { ResourceID = IDR_BUTTONUP; break; } default: return; }

Далее в зависимости от текущего состояния кнопки на момент прихода сообщения WM_DRAWITEM содержимое переменной ResourceID заменяется на идентификатор соответствующего изображения кнопки.

Для выбранной кнопки (в поле itemState структуры DRAWITEMSTRUCT установлен бит ODS_SELECTED) в переменную ResourceID записывается идентификатор изображения нажатой кнопки IDR_BUTTONDOWN.

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

После этого нужное изображение загружается из ресурсов приложения, для чего вызывается функция LoadBitmap:

hbm = LoadBitmap(hInst, MAKEINTRESOURCE(ResourceID));

Далее функция DrawButton проверяет действие, которое нужно выполнить при рисовании кнопки. Если состояние кнопки изменилось (установлен бит ODA_SELECT) и требуется перерисовать всю кнопку (установлен бит ODA_DRAWENTIRE), выполняется вызов функции DrawBitmap, определенной в нашем приложении.



Создавая функцию DrawBitmap, мы сделали так, что количество, назначение и формат ее параметров в точности соответствует функции DrawIcon, с помощью которой мы рисовали пиктограммы (вы можете легко изменить исходный текст приложения OWNBUT для того чтобы для рисования кнопки использовать не изображения bitmap, а пиктограммы; сделайте это самостоятельно). В нашем примере в качестве первого параметра функции передается контекст устройства, полученный в структуре DRAWITEMSTRUCT вместе с сообщением WM_DRAWITEM. Второй и третий параметры используются для определения координат верхнего левого угла кнопки в системе координат, связанной с родительским окном. Последний параметр - идентификатор изображения bitmap, который необходимо нарисовать:

DrawBitmap(lpInfo->
hDC, (lpInfo->
rcItem).left, (lpInfo->
rcItem).top , hbm);

После того как кнопка нарисована, следует удалить bitmap, загруженный функцией LoadBitmap. Для этого наше приложение вызывает функцию DeleteObject:

DeleteObject(hbm);

Идентификаторы кнопок и изображений bitmap определены в файле ownbut.hpp (листинг 2.6).


Содержание раздела