Войти

Показать полную графическую версию : Button со стилем BS_OWNERDRAW - класс для быстрого создания кнопки


crashtuak
29-01-2010, 19:29
Была у меня проблема создания кнопки BS_OWNERDRAW. Впринципе проблему я решил, создав для себя удобный класс. Для загрузки и отрисовки картинок использовал стороннюю либу CxImage.
class PNG_Button
{
public:
int Create(int x_pos,int y_pos,int x_sz,int y_sz,HWND parent,
LPCSTR normalimage,
LPCSTR lightedimage,
LPCSTR pushedimage)
{
x_position=x_pos, y_position=y_pos, x_size=x_sz, y_size=y_sz, B_Parent=parent;
Handle=CreateWindowEx(0,"Button",NULL,WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, x_position, y_position, x_size,y_size,B_Parent,NULL,NULL,NULL);
SetWindowLong(Handle, GWL_USERDATA, (LONG)this);
oldbtnproc=(WNDPROC)SetWindowLong(Handle, GWL_WNDPROC, (LONG)BNG_Button_Procedure);
bMouseTrack=false;

btnrect.left=x_pos,btnrect.top=y_pos,btnrect.right=x_pos+x_sz,btnrect.bottom=y_pos+y_sz;

//"E:\\TMP2\\btn_normal.png"
image[0].Load(normalimage,CXIMAGE_FORMAT_PNG);
image[1].Load(lightedimage,CXIMAGE_FORMAT_PNG);
image[2].Load(pushedimage,CXIMAGE_FORMAT_PNG);

draw=0;
if(Handle!=0)
{
//MessageBox(NULL,"Вызван конструктор PNG_Button","Диагностика",MB_OK);
return 0;
}
else
{
MessageBox(NULL,"Ошибка при создании\nкнопки.","Диагностика",MB_OK);
return -1;
}

}

int Draw()
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(Handle, &ps);
image[draw].Draw(hdc,0,0,-1,-1,0,false);
EndPaint(Handle, &ps);
return 0;
}

static LRESULT CALLBACK BNG_Button_Procedure ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
PNG_Button* pThis = (PNG_Button*)GetWindowLong(hwnd, GWL_USERDATA);
switch ( msg )
{
case WM_LBUTTONDOWN:
//MessageBox(NULL, "Pushed", "Pushed", MB_ICONHAND);
pThis->draw=2;
InvalidateRect(pThis->B_Parent,&pThis->btnrect,false);
return 0;
case WM_MOUSELEAVE:
pThis->bMouseTrack = false;
//MessageBox(NULL,"Leaved","Leaved",MB_OK);
pThis->draw=0;
InvalidateRect(pThis->B_Parent,&pThis->btnrect,false);
return 0;
case WM_LBUTTONUP:
pThis->draw=1;
InvalidateRect(pThis->B_Parent,&pThis->btnrect,false);
return 0;
case WM_MOUSEMOVE:
if(pThis->bMouseTrack==false)
{
pThis->TrackMouse(pThis->Handle);
pThis->bMouseTrack = true;
pThis->draw=1;
InvalidateRect(pThis->B_Parent,&pThis->btnrect,false);
}
return 0;
}
return CallWindowProc(pThis->oldbtnproc, hwnd, msg, wParam, lParam);
}
private:
int draw;
CxImage image[3];
RECT btnrect;
WNDPROC oldbtnproc;
HWND Handle;
HWND B_Parent;
int x_position;
int y_position;
int x_size;
int y_size;
BOOL bMouseTrack;
BOOL TrackMouse(HWND i_hwnd)
{
TRACKMOUSEEVENT treMouse;
treMouse.cbSize=sizeof(TRACKMOUSEEVENT);
treMouse.hwndTrack=i_hwnd;
treMouse.dwFlags=TME_LEAVE ;
treMouse.dwHoverTime=1;
if (!TrackMouseEvent(&treMouse))
{
MessageBox(0, "::TrackMouseEvent failed!", "Error", MB_ICONHAND);
return false;
}
else
{
return true;
}
}
};
Делаем глобальными нужное число екземпляров класса PNG_Button pngbtn1, pngbtn2;, Далее код для создания самих кнопок:
pngbtn1.Create(100,100,31,31,hWnd,
"E:\\TMP2\\btn_normal.png",
"E:\\TMP2\\btn_lighted.png",
"E:\\TMP2\\btn_pushed.png");
pngbtn2.Create(200,200,75,23,hWnd,
"E:\\TMP2\\Новая папка\\400.bmpx",
"E:\\TMP2\\Новая папка\\402.bmpx",
"E:\\TMP2\\Новая папка\\401.bmpx");
При обработке события WM_PAINT окна, в котором находятся наши кнопки, надо выполнить функции:
pngbtn1.Draw();
pngbtn2.Draw();

этому коду характерен баг »У моей кнопке такого бага нету, потому что при WM_MOUSELEAVE картинка по любому сменяется на отжатую.
ПС: это черновая версия класса для кнопки, кому будет интересно, можно будет доработать.

pva
30-01-2010, 10:14
Вообще-то BUTTON со стилем BS_OWNERDRAW должна сама вызывать WM_DRAWITEM когда необходимо. Причём обработчик WM_DRAWITEM перерисовывает кнопку полностью (сама кнопка ничего не рисует).
______________________________________________

если будешь ваять кнопку руками:
На событие нажатия ставим обработчик:

case WM_LBUTONDOWN:
case WM_LBUTONUP:
myButtonPushed = (message==WM_LBUTONDOWN); // запомнили, что надо рисовать нажатую
HDC dc = GetDC(buttonHWND); // взяли DC всего окна
myButtonPaint(dc); // перерисовали
ReleaseDC(buttonHWND, dc); // разрушили dc
if (message==WM_LBUTONUP) myButtonOnClick(); // реагируем на событие

или

case WM_LBUTONDOWN:
case WM_LBUTONUP:
myButtonPushed = (message==WM_LBUTONDOWN);
InvalidateRect(buttonHWND, 0, 0); // отрисуем потом
if (message==WM_LBUTONUP) myButtonOnClick(); // реагируем на событие
// если myButtonOnClick() выполняется долго, кнопка долго не будет перерисовываться (выглядеть нажатой)

этому коду характерен баг: если нажать кнопку и стащить указатель с кнопки, то она не отожмётся. Чтобы решить эту проблему, нужно захватить мышь:

case WM_CAPTURECHANGED: // кто-то вызвал SetCapture или ReleaseCapture
myButtonPushed = false;
InvalidateRect(buttonHWND, 0, 0); // отрисуем потом
break;

case WM_LBUTONDOWN:
myButtonPushed = true;
SetCapture(buttonHWND);
InvalidateRect(buttonHWND, 0, 0); // отрисуем потом
break;

case WM_LBUTONUP:
ReleaseCapture(buttonHWND); // вызовет WM_CAPTURECHANGED
myButtonOnClick(); // реагируем на событие
break;

case WM_PAINT:
// myButtonPushed = (GetCapture()==buttonHWND);
...

crashtuak
28-03-2010, 08:54
Доделал класс для создания BS_OWNERDRAW кнопки, смотрим шапку темы.

pva
28-03-2010, 15:24
crashtuak, ты по ходу дела не понял, как работает BS_OWNERDRAW. Саму кнопку трогать не надо, надо дописать к тому, кто её держит обработку сообщения WM_DRAWITEM и WM_MEASUREITEM. Чтобы посчитать свой размер, кнопка шлёт хозяину сообщение WM_MEASUREITEM с параметром MEASUREITEMSTRUCT, чтобы отрисоваться - сообщение WM_DRAWITEM параметром DRAWITEMSTRUCT. Всё остальное - делает сама.
Смысл в том, что у родителя есть какие-то детальки, которые он умеет рисовать и измерять. А дальше их можно запихать в BUTTON, LISTBOX, COMBOBOX и MENU. Везде будет выглядеть красиво, кроме кнопки. У ней автоматически не отрисуюется кнопочная часть.
Борландовские библиотеки субклассят все контролы, а обработку WM_*ITEM перенаправляют тому, кто его прислал (CM_*ITEM = 3000 + WM_*ITEM), в дальнейшем обёртка над кнопкой всё и отрисовывает.

crashtuak
28-03-2010, 22:28
pva, та я понял что я не понял, каким образом работает данная фича:). Я просто решил не углублятся, и пошел по пути наименшего сопротивления:)

pva
29-03-2010, 13:19
тогда предлагаю ещё меньшее сопротивление: заранее загрузить PNG и отрисовать в HBITMAP, на форме сделать кнопку со стилем BS_BITMAP.

crashtuak
29-03-2010, 13:36
pva, а полупрозрачность картинки в таком случае сохраниться?

pva
29-03-2010, 19:34
не сохранится, то и её можно отрисовать (берём фон кнопки...) А вот с красивым стилем будет облом. У меня, например, ХРюшка сразу рисует кнопку старым стилем, как только битмап ей назначишь

crashtuak
29-03-2010, 21:08
pva, а мне надо, что б у кнопки вообще небыло фона. Ну, будет время, буду чтото думать ёще. Хотя, и так работает, и довольно быстро (на слабом ноуте проверял).




© OSzone.net 2001-2012