Wątek przeniesiony 2014-02-22 01:33 z Newbie przez furious programming.

Źle obsługiwana zmiana koloru elementu ListBoxa (WM_DRAWITEM).

0

Cześć.

Mam problem z banalną sprawą. Operuje na dialogu tworzonym z zasobów. Mam taki kod jak poniżej dla obsługi WM_DRAWITEM. Mój ListBox oczywiście ma styl LBS_OWNERDRAWFIXED. I jeżeli taki sam warunek jak dla porównania Pos ze stałą HTTP_Prefix przy sprawdzaniu zaznaczenia. To po kliknięciu na element czcionka zaznaczonego elementu ze standardowej się zmniejsza.

Dla elementów odznaczonych jest ok. Bez dodatkowego if oraz else ze sprawdzaniem ListBox zachowuje się prawidłowo, ale nie mam wtedy rozróżniania kolorów. Co robię nie tak? Prosił bym o przykładowy kod pod WinAPI do rozwiązania mojego problemu. ForeColor i BackColor to zmienne zawierające domyślnie clLime oraz clBlack. Wartości wzięte ze źródel VCL, jak i funkcja RGBToColor.

Dodam, że jezeli zrobię na próbę prosty warunek w stylu if DIS.itemID mod 2 = 0 then i wtedy zrobię kolorowanie to nie ma problemu. Googlowałem, ale przykłady są raczej nie pod Delphi i albo VCL, albo jakieś naprowadzenia na C++'owe rozwiązania z MFC, co mnie nie urządza. Ale może i niedokładnie te rezultaty z Google przejrzałem.

procedure OnDrawStationsLB(DIS : PDrawItemStruct);
var
  DC : HDC;
  S : string;
  HB : HBRUSH;
begin
  DC := DIS.hDC;
  S := LBGetItemText(DIS.hwndItem, DIS.itemID);
  if (DIS.itemAction = ODA_FOCUS) and (DIS.itemState and ODS_FOCUS > 0) then
  begin
    SetTextColor(DC, ColorToRGB(ForeColor));
    SetBkColor(DC, ColorToRGB(BackColor));
    HB := CreateSolidBrush(ColorToRGB(BackColor));
  end
  else
  begin
    if Pos(Http_Prefix, TStationData(DIS.itemData).Url) = 1 then
    begin
      SetTextColor(DC, ColorToRGB(clYellow));
    end
    else
    begin
      SetTextColor(DC, ColorToRGB(ForeColor));
    end;
    SetBkMode(DC, TRANSPARENT);
    HB := CreateSolidBrush(ColorToRGB(BackColor));
  end;
  FillRect(DC, DIS.rcItem, HB);
  DeleteObject(HB);
  DrawTextEx(DC, PChar(S), Length(S), DIS.rcItem, DT_LEFT, nil);
  if DIS.itemState and ODS_FOCUS > 0 then
  begin
    DrawFocusRect(DC, DIS.rcItem);
  end;
end;

Wywołuję tę procedurę w funkcji komunikatów dialogu (wycinek) tak:

//...
    WM_DRAWITEM :
      begin
        DrawItem := Pointer(LParam);
        case LoWord(WParam) of
          IDC_STATIONSLB :
            begin
              OnDrawStationsLB(DrawItem);
              Result := 1;
              Exit;
            end;
        end;
      end;
//...

Pobieranie tekstu z Itema to taki kod:

function LBGetItemText(LBHandle : HWND; AnIndex : Integer) : string;
var
  L : Integer;
begin
  L := SendMessage(LBHandle, LB_GETTEXTLEN, AnIndex, 0);
  SetLength(Result, L);
  SendMessage(LBHandle, LB_GETTEXT, AnIndex, LParam(PChar(Result)));
end;

P.S.: poza tym powyższa zmiana skutkuje tym, że okno dialogwe programu zamyka się dopiero po drugim naciśnięciu "X" a po naciśnięciu Escape, co normalnie zamyka dialog to program się "wywala". Takich sytuacji oczywiście chciałbym uniknąć i zamknąć program.

0

@kAzek: TStationData to obiekt, który wygląda tak:

  TStationData = class(TObject)
    StationKind : TStationKind;
    NetworkCaching, Params, Url, W, P, MetaTitle : string;
  end;

Dodaję go tak, jak widzisz jako tekst pokazuje się to co siedzi w zmiennej MetaTitle z w/w klasy:

//...
            with SD do
            begin
              Idx := LBAddString(StationsLBHandle, FormatC('%s', MetaTitle));
            end;
            LBSetItemData(StationsLBHandle, Idx, SD);
//...

Funkcje dodające to:

function LBAddString(LBHandle : HWND; ItemText : string) : Integer;
begin
  Result := SendMessage(LBHandle, LB_ADDSTRING, 0, Integer(PChar(ItemText)));
end;

procedure LBSetItemData(LBHandle : HWND; Index : Integer; AData : Pointer);
begin
  SendMessage(LBHandle, LB_SETITEMDATA, Index, longint(AData));
end;

A sam ListBox ma następujący wpis w pliku *.rc:
CONTROL "", IDC_STATIONSLB, "ListBox", WS_CHILDWINDOW | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | LBS_HASSTRINGS | LBS_OWNERDRAWFIXED | LBS_SORT | LBS_NOTIFY, 8, 18, 162, 170, WS_EX_CLIENTEDGE
Ewentualnie mogę podesłać cały kod projektu na PW. Jednak nie ma się czym chwalić, bo całośc pisana trochę na szybko. Jak umiałem. I pewnie do idealnego kodu wiele jej brakuje. Problemem jest tylko to rysowanie. Że właśnie kliknięcie zmienia czcionkę na mniejszą, a próba zamknięcia dialogu czyści ListBox, a także pozwala zamknąc formatkę drugim kliknięciem w "X" i przy okazji jest crash programu ze standardowym komunikatem w moim przypadku Windows 7.

0
        case LoWord(WParam) of
          IDC_STATIONSLB :
            begin
              OnDrawStationsLB(DrawItem);
              Result := 1;
              Exit;
            end

spróbuj dodać tutaj to:

else
begin
  result:=DefWindowProc(.....);
  exit;
end;
0

@kAzek: podesłałem źródła na PW. Mam nadzieję, że dojdziesz co jest nie tak. Bo ja słabo pod WinAPI ogarniam obsługę kolorowania po swojemu ListBoxa i ogólnie kontrolek. Także działałem trochę po omacku w tym przypadku :/

@Azarien: niestety to nie pomaga. Zachowanie takie samo ze wspomnianymi błędami. Zresztą, jeżeli dobrze zrozumiałem opis WM_DRAWITEM na MSDNie, to jeżeli obsługujemy ten komunikat, musimy zwrócić True. A że ta funkcja obsługi komunikatów musi zwracać też prawidłowe HBRUSH dla kolorowania pozostałych kontrolek, jest ona z typem wynikowym jako LRESULT. Także zwracam 1.

Anyway, jeżeli ktoś na coś jeszcze wpadnie, to piszcie. Póki co czekam, może @kAzek coś wymodzi z kodem, który mu podesłałem. Wtedy podzielę się tutaj rozwiązaniem, o ile będzie to łatwe do w miarę krótkiego przedstawienia. Chociaż podejrzewam, że rozwiązanie jest banalne, dlatego wątek był na początku w `Newbie. A po prostu ja czegoś nie wiem. Gdyż ciągle doskonale swoją niewielką widzę o WinAPI.

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