Рисуем кнопку
Как я уже говорил, не все элементы окна можно обработать при помощи сообщений типа WM_CTLCOLOREDIT (см. Устанавливаем цвет элемента окна). К таким элементам относится и кнопка. Чтобы справится с этой проблемой нужно применить другую более мощную методику. Используя ее мы сможем не только менять цвет элемента и цвет текста, а также рисовать и помещать на элемент графическое выражение и наконец, даже менять форму элемента окна. В этой статье рассмотрим сам подход и разберем конкретный пример с кнопкой. Пример очень прост: окно и кнопка выхода. При щелчке мышью по кнопке кнопка и надпись на ней меняет свой цвет. При отпускании цвета восстанавливаются, правда в данном примере уыидеть это сложно, так как окно сразу закрывается. Вы можете по эксперементировать с примером и проверить мое утверждение.

#include <Windows.h>
LRESULT CALLBACK winproc( HWND, UINT, WPARAM, LPARAM);
WNDCLASSEXW ws; //струкутра для регистрации класса окон
wchar_t * wn = L"class1"; //имя класса
MSG msg; //для сообщения
HWND hwnd,hb1; //для дескриптора окна и кнопки
int pr;
HINSTANCE h; int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
h=hInstance;
ws.cbSize = sizeof(WNDCLASSEXW); ws.cbClsExtra = 0;
ws.cbWndExtra = 0; ws.hIcon = LoadIcon( NULL, IDI_APPLICATION );
ws.hCursor = LoadCursor( NULL, IDC_ARROW );
ws.hbrBackground = CreateSolidBrush(RGB(0, 80, 80));
ws.hIcon = NULL; ws.hInstance = hInstance;
ws.lpszClassName = wn; ws.lpszMenuName = NULL;
ws.lpfnWndProc = (WNDPROC)winproc; ws.style = CS_VREDRAW|CS_HREDRAW;
//зарегистрировать класс окна
if(RegisterClassExW(&ws)==0){
MessageBoxExW(0,L"Ошибка регистрации!",L"Сообщение", 0, 0);
return 0;
};
//создать окно
hwnd= CreateWindowExW(0, wn,
L"Окно в 64-битово системе!", WS_OVERLAPPEDWINDOW,
10,10,400,300,NULL,NULL,hInstance, NULL);
//сделать окно видимым
ShowWindow(hwnd, nCmdShow);
//обновить содержимое
UpdateWindow(hwnd);
//цикл обработки сообщений
while (GetMessage( &msg, NULL, NULL, NULL)) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
//выход из программы
return (int)msg.wParam;
}
//функция окна
LRESULT CALLBACK winproc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPDRAWITEMSTRUCT l;
HBRUSH hc; //дескриптор кисти
RECT textRect;
HRGN bgRgn;
switch ( uMsg ) {
case WM_CREATE:
pr=0;
//создать кнопку
hb1=CreateWindowExW(0,L"button", L"Жми",
WS_CHILD | WS_VISIBLE| BS_OWNERDRAW, 10, 10, 80, 30, hWnd, 0, h, NULL);
break;
case WM_COMMAND:
if(lParam==(int)hb1){
if(HIWORD(wParam)==BN_CLICKED){
DestroyWindow(hWnd);//закрыть приложение
}
}
break;
case WM_DRAWITEM:
l = (LPDRAWITEMSTRUCT)lParam;
if(hb1==(l->hwndItem)){
if(l->itemState & ODS_SELECTED){
//кнопка перерисовывается
hc=CreateSolidBrush(RGB(255, 255, 0));
bgRgn = CreateRectRgnIndirect(&l->rcItem);
FillRgn(l->hDC, bgRgn, hc);
SetTextColor(l->hDC,RGB(0,55,55));
SetBkColor(l->hDC,RGB(255,255,0));
DrawText(l->hDC,L"Жми",-1,&l->rcItem, DT_CENTER );
pr=1;
break;
}
if(l->itemAction==ODA_DRAWENTIRE||pr==1){
//кнопка нажимается
hc=CreateSolidBrush(RGB(255, 0, 0));
pr=0;
bgRgn = CreateRectRgnIndirect(&l->rcItem);
FillRgn(l->hDC, bgRgn, hc);
SetTextColor(l->hDC,RGB(100,100,100));
SetBkColor(l->hDC,RGB(255,0,0));
DrawText(l->hDC,L"Жми",-1,&l->rcItem, DT_CENTER );
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
};

Сама структура программы вполне стандартна и я уже неоднократно разбирал ее элементы. Так что приступим сразу к изучению того, что связано с рисованием кнопки. Главное здесь вот что. При создании кнопки я использую добавочный стиль BS_OWNERDRAW. Это то и есть центральный момент примера. После этого на функцию окна начинают приходить сообщения WM_DRAWITEM. Это волнующий момент. Теперь с кнопкой можно делать, все что не заблагорассудится. По приходу сообщения WM_DRAWITEM lParam будет указывать на структуру DRAWITEMSTRUCT. С ней я и работаю дальше. Не буду описывать все элементы струкутры, их описание можно найти вот здесь. Укажу только на то, что нам интересно. Поле hwndItem - дескриптор элемента. Это важно, поскольку в окне могут быть несколько элементов, обрабатываемых таким образом. Далее - itemState. Данное поле определяет состояние, после того как произошла перерисовка. Т.е. если произошла перерисовка, а элемент выбран - щелкнули мышкой, то вот теперь мы меняем его свойства, т.е. перерисовываем снова. Однако, после того, как отпускаем кнопку мыши, приходят новые сообщение о перерисоке. И тут нужно вернуть все цвета обратно. В этом в первую очередь нам помогает переменная-флаг pr. Отследите, как она работает. Еще один элемент струкутры DRAWITEMSTRUCT это itemAction. Мы его используем как индикатор того, что кнопку надо перерисовать. Еще одно поле, без которого мы бы не узнали размеры области перерисовки - rcItem, это просто прямоугольник. Мы его используем для создания объекта область - bgRgn (CreateRectRgnIndirect), который затем закрышиваем (FillRgn). Наконец hDC - дескриптор контектса окна, в данном случае кнопки (кнопка ведь тоже окно). С помощью этого дескриптора можно выводить информацию на кнопку, что я и делаю в программе. Не буду комментировать API-функции, их название и параметры говорят сами за себя.

Конечно, это еще не все по рисованию элементов. Хочется рассмотреть еще кнопку с картинкой и кнопку произвольной формы. Но это в будущих этюдах.

Ну вот пока все. Наслаждайтесь программированием.

 Назад       Заметки       Сайт       Страница-портал       Журнал
(c) Copyright Владислава Юрьевич Пирогов