Файл 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).