Войти

Показать полную графическую версию : Как правильно "запихнуть" оконную процедуру в самодельный класс?


crashtuak
14-02-2010, 10:03
Как правильно "запихнуть" оконную процедуру в самодельный класс, и как потом правильно прикрутить ту процедуру к нужному окну(в данном случае-к кнопке)? Вот собственно код класса, прошу строго не судить, мой первый класс в жизни:)(который без оконной процедуры работает нормально):
class PNG_Button
{
public:
HWND bt_hwnd;
HWND bt_parent_hwnd;
int x_position;
int y_position;
int x_size;
int y_size;
LPCSTR normal_image;
LPCSTR focused_image;
LPCSTR pushed_image;
LPCSTR draw_image;
BOOL g_bMouseTrack;
WNDPROC btnOld;
//вот она-процедура
LRESULT CALLBACK ButtonSubclassProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_MOUSELEAVE:
{
g_bMouseTrack = FALSE;
draw_image=normal_image;
InvalidateRect(bt_parent_hwnd,0,true);
}
return 0;
case WM_MOUSEHOVER:
{
draw_image=focused_image;
InvalidateRect(bt_parent_hwnd,0,true);
}
return 0;
case WM_LBUTTONDOWN:
{
draw_image=pushed_image;
InvalidateRect(bt_parent_hwnd,0,true);
}
return 0;
case WM_LBUTTONUP:
{
draw_image=focused_image;
InvalidateRect(bt_parent_hwnd,0,true);
}
return 0;

case WM_MOUSEMOVE:
if(g_bMouseTrack==FALSE)
{
TrackMouse(bt_hwnd);
g_bMouseTrack = TRUE;
}

return 0;
}
return CallWindowProc(btnOld, hwnd, msg, wParam, lParam);
}
//конец оконной процедуры

//конструктор
PNG_Button(LPCSTR bt_text,int x_pos,int y_pos,int x_sz,int y_sz,
HWND bt_parent,
LPCSTR n_image,
LPCSTR f_image,
LPCSTR p_image)
{
x_position=x_pos, y_position=y_pos, x_size=x_sz ,y_size=y_sz;
draw_image=n_image, normal_image=n_image, focused_image=f_image, pushed_image=p_image;
bt_parent_hwnd=bt_parent;
g_bMouseTrack = FALSE;
bt_hwnd=CreateWindowEx(NULL,"Button",bt_text,WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, x_pos, y_pos, x_sz, y_sz,bt_parent, NULL,NULL,NULL);
//на слудующую строчку очень сильно ругается компилятор
btnOld = (WNDPROC)SetWindowLong(bt_hwnd, GWL_WNDPROC, (LONG)ButtonSubclassProc);
TrackMouse(bt_hwnd);
return 1;
}
//конец конструктора
HWND Get_BT_HWND()
{
return bt_hwnd;
}
int DrawBTN()
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(bt_hwnd, &ps);
CxImage image;
image.Load(draw_image,CXIMAGE_FORMAT_PNG);
image.Draw(hdc,0,0,-1,-1,0,true);
EndPaint(bt_hwnd, &ps);
return 1;
}

};

ganselo
14-02-2010, 17:42
Как правильно "запихнуть" оконную процедуру в самодельный класс »
Как вариант сделать её френдом класса. А можно попробовать сделать её статической:

static LRESULT CALLBACK ButtonSubclassProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )


и как потом правильно прикрутить ту процедуру к нужному окну »
В конструкторе например:

PNG_Button()
{
//не помню полей структуры btnOld, но примерно так
btnOld.proc = ButtonSubclassProc;
}

crashtuak
19-02-2010, 15:38
ganselo, если делать статической, то пропадает доступ к членам класа. Можно подробнее про френд класа? Я вроде бы читал, что френдом класа может быть только другой клас...

ganselo
19-02-2010, 16:13
Вот простенький пример:

class test
{
public:
testint _a, int _b) : a(_a), b(_b) {}
private:
int a, b;
friend int summa(const test &t);
};

int summa(const test &t)
{
return t->a + t->b;
}

int main()
{
test t(10, 20);
cout << summa(t);
return 0;
}


Что такое функция френд? Эта функция которая не является методом класса, но при этом имеет доступ к закрытым методам класса.

crashtuak
19-02-2010, 16:41
ganselo, хм, интересно. Но так не пойдет, потому что функция-френд будет оконной процедурой, для которой добавить еще один аргумент для передачи указателя на наш клас не есть возможным. Надо как то запихнуть процедуру непосредствено в клас...

pva
23-02-2010, 00:09
передача указателя решается несколькими путями:
1. через отображение HWND -> my_class* (самый защищённый)
где-то хранится массив пар HWND, my_class*; функция WindowProc ищет в этом массиве свой HWND и для найденного(ных) указателя(лей) выполняет какой-нить виртуальный my_class::__wndproc
2. установка SetWindowLong (ИМХО самый экономный)
для субклассинга лучше использовать GWL_USERDATA, хотя можно и зарегистировать новый класс на основании старого, только увеличить cbHwndExtra (или как его там), и хранить в нём нужную информацию
3. через свойства SetProps (ну тоже хороший способ)
главное угадать такое название свойства, которое ещё никто не придумал. Прикрутить к этому названию свой указатель. Я замерял производительность - после 4 способа - самый быстрый, название свойства брал reinterpret_cast<wchar_t*>(1).
4. через сгенерированную в памяти функцию (самый быстроработающий)
для каждого окна в памяти выделяется блок 32 байта, в который пишется машинный код

mov eax, указатель_на_класс
mov[esp+4], eax
jmp WindowProc
nop
nop
nop

Окну в качестве процедуры выставляется адрес этих 32 байт
теперь у оконной процедуры вместо HWND будет указатель_на_класс. Этот способ используется во многих библиотеках. Недостаток: для правильной работы требуется правильно расположить в памяти. Например если эти 32 байта взять внутри my_class, оно может не работать (у меня не стало, а то же самое, во вновь выделенной памяти - на ура)




© OSzone.net 2001-2012