Problem z rysowanym Buttonem, z Ikoną HICON w WinAPI.

0

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.

3

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

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 :)

0

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 :).

0

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 :)

0

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;
0

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.

0

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.

0
  1. gdzie masz obsługę WM_DRAWITEM
  2. ustawiasz Result na true?
    BTW daj gotowe źródła, w których można pogrzebać będzie prościej :)
0

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 :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0