Перерисовка Radio и Check баттонов
Извиняюсь, неправильно прочитал вопрос, так вот вам надо указать в качестве стиля BS_OWNERDRAW, а потом ловить у главного окна сообщение WM_DRAWITEM подробнее смотри в справке
Прочитал справку, и все равно не понял как сделать нормальную радио-баттон используя OWNERDRAW. Получается придется создавать глобальные переменные для определения checked\unchecked? Хотелось бы получить полный функционал radio-button, при этом просто перерисовывая её
Читай тут :
owner-drawn button, combo box, list box, or menu when a visual
aspect of the button, combo box, list box, or menu has changed.
WM_DRAWITEM
idCtl = (UINT) wParam; // control identifier
lpdis = (LPDRAWITEMSTRUCT) lParam; // item-drawing information
The DRAWITEMSTRUCT structure provides information the owner window
must have to determine how to paint an owner-drawn control or menu item.
The owner window of the owner-drawn control or menu item receives a
pointer to this structure as the lParam parameter of the WM_DRAWITEM
message.
typedef struct tagDRAWITEMSTRUCT { // dis
[COLOR="Red"]UINT CtlType; [/COLOR]
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
[COLOR="Red"]HWND hwndItem; [/COLOR]
[COLOR="Red"]HDC hDC;[/COLOR]
RECT rcItem;
DWORD itemData;
} DRAWITEMSTRUCT;
Искал что такое субклассинг, оказалось это то, что назяваю подменой оконной процедуры :D.
Попробовал, и вот что получилось:
var ps : TPaintStruct;
begin
case msg of
WM_DESTROY : PostQuitMessage(0);
[COLOR="Red"]BM_GETCHECK :
begin
result := defProc(wnd, msg, wparam, lparam);//Вначале вызываем
//оригинальный обработчик
InvalidateRect(wnd, nil, true);.//А затем перерисовываем все окно
end;[/COLOR]
WM_PAINT :
begin
//Ну рисовать вы умеете
BeginPaint(wnd, ps);
if SendMessage(wnd, BM_GETCHECK, 0, 0) = 0 then
FillRect(ps.hdc, ps.rcPaint, GetSysColorBrush(COLOR_BTNSHADOW))
else
FillRect(ps.hdc, ps.rcPaint, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
EndPaint(wnd, ps);
end;
else
result := defProc(wnd, msg, wparam, lparam);
end
end;
Я так понял у вас была проблема с неполной перерисовкой, вот она и решена, только обязатель вызывайте InvalidateRect с параметром true - а то окно будет моргать.
Хммм. Рано радовался. Вообщем суперклассирую свои радио буттоны (суперклассинг - создание класса на основе другого класса, в моем случае на основе BUTTON)
Итак, код (сорри, но данный проект пишу на асме:) )
//wcRadio - переменная содержит имя нового класса "RADIOBUTTON"
WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL ps :PAINTSTRUCT
LOCAL rc :WNDCLASSEX
.IF uMsg == WM_DESTROY
...
.ELSEIF uMsg == WM_CREATE
; Createing "RADIOBUTTON" superclass:
mov rc.cbSize, sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr szButton,addr rc
push rc.lpfnWndProc
pop OldRadioProc
mov rc.lpfnWndProc, OFFSET RadioProc
push hInstance
pop rc.hInstance
mov rc.lpszClassName, OFFSET wcRadio
invoke RegisterClassEx, addr rc
invoke CreateWindowEx, 0, addr wcRadio, NULL, WS_CHILD+WS_VISIBLE+BS_AUTORADIOBUTTON, 200, 200, 14, 14, hWnd, NULL, hInstance, NULL
mov rb, eax
invoke CreateWindowEx, 0, addr wcRadio, NULL, WS_CHILD+WS_VISIBLE+BS_AUTORADIOBUTTON, 200, 250, 14, 14, hWnd, NULL, hInstance, NULL
mov rb2, eax
... //обработка других сообщений
Далее, моя процедура обработки (RadioProc):
LOCAL dc :HDC
LOCAL ps :PAINTSTRUCT
.IF uMsg == BM_GETCHECK
invoke CallWindowProc, OldRadioProc, hWnd, uMsg, wParam, lParam
push eax
invoke InvalidateRect, hWnd, NULL, TRUE
.ELSEIF uMsg == WM_PAINT
invoke BeginPaint, hWnd, addr ps
mov dc, eax
invoke SendMessage, hWnd, BM_GETCHECK, 0, 0
.if (eax == 0)
invoke BitBlt, dc, 0, 0, 14, 14, r1DC, 0, 0, SRCCOPY
.else
invoke BitBlt, dc, 0, 0, 14, 14, r2DC, 0, 0, SRCCOPY
.endif
invoke EndPaint, hWnd, addr ps
.ELSE
invoke CallWindowProc, OldRadioProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
pop eax
ret
RadioProc endp
Вот у меня выводится две радио кнопки. Все бы ничего, но проблема в том, что первая кнопка работает нормально, а вторая хоть и рисуется, но при нажатии на неё, поверх рисуется стандартная виндосская радио кнопка. В чем может быть проблема?
.IF uMsg == BM_GETCHECK
[COLOR="Red"]invoke CallWindowProc, OldRadioProc, hWnd, uMsg, wParam, lParam[/COLOR]
push eax
invoke InvalidateRect, hWnd, NULL, TRUE
.ELSEIF uMsg == WM_PAINT
invoke BeginPaint, hWnd, addr ps
[COLOR="Green"]mov dc, eax
invoke SendMessage, hWnd, BM_GETCHECK, 0, 0
.if (eax == 0)
invoke BitBlt, dc, 0, 0, 14, 14, r1DC, 0, 0, SRCCOPY
.else
invoke BitBlt, dc, 0, 0, 14, 14, r2DC, 0, 0, SRCCOPY
.endif[/COLOR]
invoke EndPaint, hWnd, addr ps
Дело в том что при вызове оригинального обработчика окна винда перерисовывает окно используя GetDC() Поэтому в WM_PAINT нужно заново обрисовать окно - т.е прийдется вызывать и GetWindiwText и FillRect и еще бог знает что, а еще попробуйте в InvalidateRect пооследний параметр вызвать с false (может я что напутал?)
зы. Ассемблером нас не напугаешь ;)
Конечно извиняюсь за назойливость, но к сожалению в инете не нашел вообще ничего по этой теме.
Конечно извиняюсь за назойливость, но к сожалению в инете не нашел вообще ничего по этой теме.
Ну вот так наверное,
buff : array [0..255] of char;//ну это вроде кака участок памяти на 256 байт
Textlength : Cardinal;
begin
BeginPaint(wnd, ps);
//Закрашиваем всю область
FillRect(ps.hdc, ps.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
//это ваша строчка
BitBlt(ps.dc, 0, 0, 14, 14, r1DC, 0, 0, SRCCOPY);
//Получаем заголовок и его длинну в символах
Textlength := GetWindowText(wnd, buff, 256);
//Устанавливаем шрифт
SelectObject(ps.hdc, GetStockObject(DEFAULT_GUI_FONT));
//И выводим
TextOut(ps.hdc, 16, 0, buff, Textlength);
EndPaint(wnd, ps);
end;
end;
Если это не поможет то лучше создайте свой класс а насчет хранения состоянний (чек/унчек) могу посоветовать
GetWindowLong и SetWindowLog со значением GWL_USERDATA, я вооще по каждому случаю свои классы пишу, может это и не правильно, зато детям нравится :)
PS: код все-таки не помог
Отлавливай BM_SETCHECK, отдавай стандартной процедуре, потом рисуй свою кнопь.
ПыСы. Вообще не понимаю, зачем BM_GETCHECK обрабатывать..
Итак, готовое решение:
LOCAL ps :PAINTSTRUCT
.IF uMsg == BM_SETCHECK
invoke CallWindowProc, OldRadioProc, hWnd, uMsg, wParam, lParam
push eax
invoke InvalidateRect, hWnd, NULL, FALSE
.ELSEIF uMsg == WM_PAINT
invoke BeginPaint, hWnd, addr ps
invoke GetSysColorBrush, COLOR_BTNFACE
invoke SendMessage, hWnd, BM_GETCHECK, 0, 0
.if (!eax)
invoke BitBlt, ps.hdc, 0, 0, 14, 14, r1DC, 0, 0, SRCCOPY
.else
invoke BitBlt, ps.hdc, 0, 0, 14, 14, r2DC, 0, 0, SRCCOPY
.endif
invoke EndPaint, hWnd, addr ps
.ELSEIF (uMsg == WM_LBUTTONDOWN)||(uMsg == WM_MOUSEMOVE)||(uMsg == WM_LBUTTONDBLCLK)
invoke CallWindowProc, OldRadioProc, hWnd, uMsg, wParam, lParam
push eax
invoke InvalidateRect, hWnd, NULL, FALSE
.ELSE
invoke CallWindowProc, OldRadioProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
pop eax
ret
RadioProc endp
WM_LBUTTONDOWN и WM_LBUTTONDBLCLK и WM_MOUSEMOVE обрабатываются для избежания артефактов при прорисовке. Пойду-ка напишу статью, чтобы на мои грабли никто больше не наступал:D
ПыСы. Вообще не понимаю, зачем BM_GETCHECK обрабатывать..
А вы уверены что если GETCHECK не обработается оригинальной функцией, то состояние кнопки будет меняться - чек/унчек?
А GETCHECK обрабтывается потому, что после его обработки не посылается сообщение WM_PAINT, а происходит перерисовка через GetDC()
zb). Ждем статьи
Мы же и так отдаем все сообщения оригинальной фукции! Я имел ввиду зачем его в нашем обработчике обрабатывать дополнительно..
не, все-равно не понял :))
ПыСы. Статья - это самое достойное добивание решившейся проблемы! :D
не, все-равно не понял :))
[COLOR="Gray"]Решил программист спрыгнуть с крыши - все посчитал, разбежался и с криком "Ой мля знак перепутал!!!" улетел в небо[/COLOR]
Это конечно мне не оправдание :(.
На самом деле надо было обрабатывать BM_SETCHECK. изменить состояние кнопки можно только послав ей это сообщение и ни как иначе. Можно конечно обрабатывать все сообщения от мыши - WM_LB* и вызывать BM_SETCHECK, но в случае с радио-кнопками если с одной из них снимается значение, то никаких сообщений от мыши не посылается, тоесть нам просто необходимо отлавливать именно момент изменения значения кнопки.
зы. Надеюсь я меня поняли. И еще раз извиняюсь за неверную информацию.