Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Button со стилем BS_OWNERDRAW - класс для быстрого создания кнопки (http://forum.oszone.net/showthread.php?t=165579)

crashtuak 29-01-2010 19:29 1333739

Button со стилем BS_OWNERDRAW - класс для быстрого создания кнопки
 
Была у меня проблема создания кнопки 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();


Цитата:

Цитата pva
этому коду характерен баг »

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

pva 30-01-2010 10:14 1334074

Вообще-то 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 1378899

Доделал класс для создания BS_OWNERDRAW кнопки, смотрим шапку темы.

pva 28-03-2010 15:24 1379076

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 1379379

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

pva 29-03-2010 13:19 1379745

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

crashtuak 29-03-2010 13:36 1379768

pva, а полупрозрачность картинки в таком случае сохраниться?

pva 29-03-2010 19:34 1380075

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

crashtuak 29-03-2010 21:08 1380137

pva, а мне надо, что б у кнопки вообще небыло фона. Ну, будет время, буду чтото думать ёще. Хотя, и так работает, и довольно быстро (на слабом ноуте проверял).


Время: 07:50.

Время: 07:50.
© OSzone.net 2001-