Cześć. Zwykle sam sobie radzę - jak wiecie. Jednak tutaj kombinowałem i nie mogę przeskoczyć jednego problemu. Chodzi o kod, który wrzuciłem na http://4programmers.net/Pastebin/2257 - a dodatkowo z programem testującym jest dołaczony do tego posta. Całość ma docelowo działać w WinAPI i chodzi mi tutaj o kod unit'u zawarty w archiwum, w pliku winapi_iconbtn.pas
. Mam problem z przypisaniem innego handle ikonki dla buttona albo jego wyzerowaniu (własność IconHandle
typu HICON
). Jeżeli ktoś z Was ogarnia lepiej WM_DRAWITEM
oraz WinAPI to proszę o pomoc i przykładowe rozwiązania z kodem. Z góry dziekuję za wszelką pomoc.
eee nie bardzo kumam o co chodzi :p
Nie możesz zrobić tak
WaBtn.IconHandle := Ico.Handle;
Ico.Free;
WaBtn.Caption := GenerateRandomString(RandomRange(6, 20));
, tzn. przypisać uchwyt ikony a potem ją usunąć. Zaremuj Icon.free
i wsio działa :)
A wyzerowanie to przypisanie 0
do IconHandle
. Zmień też SetIconHandle
tak aby nie robić DestroyIcon
jeśli FIconHandle
= 0
@abrakadaber: dziękuję za pomoc i nakierowanie. Jednak pisanie po nocach to nie zawsze dobry pomysł. Dopiero po Twoim nakierowaniu i odespanniu nocy - poprawiłem błędy i dopracowałem kod do końca. Oczywiście to Free
, było błędne. Natomiast "ikonkowy Glyph" oraz Caption
znikały po zmianie, dlatego iż w procedurze CalcButtonLayout
nie zerowałem koordynatow zmiennej GlyphSize
. Teraz wszystko gra. A komponent działa i zachowuje się dokładnie tak, jak tego chcę.
Przykładowy kod dołaczam wraz z exekiem do tego posta. Dodatkowo sam kod komponentu jest wkleiłem bez czasu ważności (nigdy) pod ten adres: http://4programmers.net/Pastebin/2260 - mam nadzieję, że komuś poza mną się on przyda. Jest to dobra baza do pisania innych komponentów pod WinAPI w przyszłości. Jednak wiadomo, że bez przykładowych fragmentów kodu "podkradniętych" ze źródeł VCL z instalki Delphi 7 Enterprise raczej nie byłbym w stanie ogarnąć samodzielnie takiego kodu jak choćby funkcji MakeObjectInstance
.
Także ode mnie @abrakadaber +1 ode mnie za naprowadzenie na rozwiązanie. A swój post zatwierdzam jako kompletne rozwiązanie. Raz, bo mogę ;) A dwa - podaje tutaj działający kod, pozbawiony problemów, opisanych przeze mnie na początku tego wątku :)
w poprzednim kodzie dziwnie się zachowywało zaznaczenie, że guzik jest aktywny - jeden klik rysował zaznaczenie (przerywana obwódka na brzegach guzika) a drugi nie rysował. Nie wiem czy teraz jest to problem bo po kliku traci focus :).
Spoko. Jeżeli ogarnę, to może dopracuje odpowiednie reagowanie na Focus
. Ponieważ jak dla mnie, to ten przycisk wcale nie musi mieć obsługi Focusa. Zresztą nie implementowałem żadnego SetFocus
jak widzisz. Akutalnie jego wygląd i zachowanie jest dla mnie zadowalające. Chodziło mi głownie o: wyświetlanie w sposób prawidłowy ikonki koniecznie ze zmiennej typu HICON
oraz tekstu. Tak, jak potrafi to robić TBitBtn
. A całośc ma być dla WinAPI. Zresztą jak można się domyśleć, część kodu do ustalania położenia "glypho - ikony" oraz tekstu, zapożyczyłem sobie z przykładowych kodów źródłowych VCL wspomnianej klasy :)
Odświeżam temat, bo wrociłem do tego kodu - aktualną wersję wklejam pod adresem: http://pastebin.com/9h151mv9 - cofnąłem też akceptacje rozwiązania problemu w tym wątku. Otóż pojawił się nowy problem. Chciałbym aby komponent uzupełniał procedurę obsługi komunikatów dla okna rodzica. Czyli żeby kontrolka wiedziała kiedy okno rodzica otrzymuje WM_ITEMDRAW
. Jednak teraz działa to tylko dla jednej kontrolki na ekranie. Próba utworzenia kolejnej skutkuje tym, że ta pierwsza znika. Podejrzewam, że to przez nadpisanie obsługi WM_DRAWITEM
. Zobaczcie działanie dołaczonego exeka, po naciśnięciu na przycisk z ikonką odtwarzania i napisem "WINAPI" Natomiast rysowanie buttona przez obslugę WM_PAINT
nie bardzo mi wychodzi. Bo nie umiem narysowac nieaktywnego buttona, klikniętego lub tła pod napisem w odpowiednich kolarach. Z użyciem przeakazanej struktury w typie PDrawItemStruct
jest to o wiele prostsze do ogarnięcia. Przy okazji też należało by dopracować destruktor. Także podsumowując. Docelowo całość ma działać w WinAPI. Czyli jak dodać obslugę WM_DRAWITEM do funkcji obsługi komunikatów okna dialogowego (lub na przykład formatki) bez usuwania poprzedniego. Proszę o pomoc w rozwiązaniu tego problemu. Może też ktoś poza Userem @kAzek będzie umiał coś doradzić. Bo wiadomo CN_DRAWITEM
obsługują kontrolki VCL. Natomiast mi, jak wspomniałem, zależy na przycisku z ikonka podawaną jako typ HICON i rozwiązaniem działającym w WinAPI. Przykładowe kodu mile widziane. W tym ewentualne rysowanie przez WM_PAINT
. Póki co WM_PAINT
wyglądało u mnie tak, ale efekt - jak wspominałem powyżej - mało elegancki. Zakomentowałem to co wedlug mnie zbędne, a dla WM_PAINT
, to co nie wiem do końca jak obsłużyć.
procedure TWAIconBtn.WndProc(var AMessage : TMessage);
var
FDrawItem : PDrawItemStruct;
const
Icon_Param_Arr : array[boolean] of UINT = (0, DSS_DISABLED);
var
H : HWnd;
DC : HDC;
R : TRect;
PaintS : TPaintStruct;
OldFont : HFONT;
IconPos, TxtPos : TPoint;
begin
with AMessage do
begin
case Msg of
WM_PAINT :
begin
H := Self.Handle;
GetClientRect(H, R);
DC := BeginPaint(H, PaintS);
CalcButtonLayout(DC, R, FIconHandle,
FCaption, FAppFont, FLayout, Btn_Margin, Btn_Spacing, IconPos, TxtPos);
DrawState(DC, 0, nil, FIconHandle, 0,
R.Left + IconPos.X,
R.top + IconPos.Y, 0, 0, DST_ICON or
Icon_Param_Arr[not IsWindowEnabled(FHandle)]);
OldFont := SelectObject(DC, FAppFont);
if not IsWindowEnabled(FHandle) then
begin
SetTextColor(DC, GetSysColor(COLOR_BTNSHADOW));
if FCaption <> '' then
begin
TextOut(DC, TxtPos.X, TxtPos.Y, PChar(FCaption), Length(FCaption));
end;
end
else
begin
if FCaption <> '' then
begin
TextOut(DC, TxtPos.X, TxtPos.Y, PChar(FCaption), Length(FCaption));
end;
end;
{
if (ParamDrawItem.itemState and ODS_SELECTED) <> 0 then
begin
DrawEdge(ParamDrawItem.HDC, ParamDrawItem.rcItem, EDGE_SUNKEN, BF_RECT);
end
else
begin
DrawEdge(ParamDrawItem.HDC, ParamDrawItem.rcItem, EDGE_RAISED, BF_RECT);
end;
}
if GetFocus = FHandle then
begin
InflateRect(R, -4, -4);
DrawFocusRect(DC, R);
end;
SelectObject(DC, OldFont);
EndPaint(H, PaintS);
end;
WM_OWNERDRAW :
begin
// FDrawItem := Pointer(LParam);
// DrawBitBtn(FDrawItem, FIconHandle, FCaption);
end;
wM_SETCURSOR :
begin
if IsWindowEnabled(FHandle) then
begin
SetCursor(LoadCursor(0, IDC_HAND));
AMessage.Result := 0;
Exit;
end;
end;
WM_LBUTTONUP :
begin
if Assigned(FOnClick) then
begin
FOnClick(Self);
end;
end;
WM_RBUTTONUP :
begin
DoRightClick(Self);
end;
end;
if POldButtonProc <> nil then
begin
Result := CallWindowProc(POldButtonProc, Self.Handle, Msg, WParam, LParam);
end;
end;
end;
Czy mógłbyś mnie wytłumaczyć czemu schodzisz tak nisko z obsługa?
Weź sobie kod od np TBitBtn i przerób go tylko w obrębie DoPaint.
Kombinuje tak, ponieważ na bazie obsługi WM_DRAWITEM
miałem efekt, który wyglądał jak należy. A i jak wcześniej wspomniałem, chciałbym to mieć działające pod czystym WinAPI. Poza tym nie widzę w źródłach VCL pod Delphi 7, w klasie TBitBtn
metody DoPaint
. Są takowe ale dla TToolButton
. Jeżeli ktoś ma jeszcze pomysły jak rysować taki Button jak pokazałem w dołaczonym kodzie, ale samym WM_PAINT
i mieć obslugę Enabled i zmian położenia oraz tekstu to proszę piszcie.
- gdzie masz obsługę
WM_DRAWITEM
- ustawiasz
Result
natrue
?
BTW daj gotowe źródła, w których można pogrzebać będzie prościej :)
Sorry, że tak odświeżam ten wątek. Ale tak to jest jak się pisze kod po nocach i nie dokładnie wszystko sprawdzi. Dopiero teraz działa do końca tak jak chciałem. Poprawiony i mam nadzieję - ostateczny załącznik dodaję do tego posta. Jeżeli coś się znowu zmieni to chyba odpuszczę sobie powiadamianie Was o tym, a bardziej wprawni z Was, z pewnością i tak sobie poradzą z dopasowaniem kodu do swoich potrzeb, gdyby coś nie chodziło jak trzeba :)