TCheckListBox - problem z usuwaniem itemów o zmiennej wysokości

TCheckListBox - problem z usuwaniem itemów o zmiennej wysokości
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 11 godzin
  • Lokalizacja:Tuchów
  • Postów:12175
0

Piszę program, w którym wykorzystuję komponent z klasy TCheckListBox z ustawionym stylem na lnOwnerDrawVariable; Wszystko wszło gładko (ustawianie wysokości poszczególnych itemów, malowanie itd), ale problem pojawił się podczas usuwania zaznaczonych itemów;

Otóż lista wygląda podczas działania programu mniej więcej tak:

ListBeforeDelete.png

Ale jeśli usunę kilka item'ów zostaje zmieniona wysokość niektórych (nie określę dokładniej których) zmienia się, gdzie rysowany tekst znacznie wykracza poza rect'a itema, np. tak:

ListAfterDelete.png

Czy ktoś wie dlaczego tak się dzieje i jak można temu zaradzić? Usuwanie realizuję przez prostą pętlę:

Kopiuj
var
  I: Integer;
begin
  { po sprawdzeniu warunków }
  for I := List.Items.Count - 1 downto 0 do
    if List.Checked[I] then
      List.Items.Delete[I];

  {...}

Z niczym nie mam problemu, tylko z usuwaniem zaznaczonych itemów;


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
LN
  • Rejestracja:około 16 lat
  • Ostatnio:około rok
  • Postów:1398
1

A w jaki sposób ustawiasz wysokość poszczególnych itemów ? Pewnie po usunięciu musisz to "refreshnąć" dla wszystkich, które pozostały (i są na pozycji n i większej, gdzie n to usuwany element)

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 11 godzin
  • Lokalizacja:Tuchów
  • Postów:12175
0
[losowa nazwa] napisał(a)

A w jaki sposób ustawiasz wysokość poszczególnych itemów ?

Najpierw wczytuję odpowiednie dane do macierzy rekordowej z pliku; Później obliczam ilość linii samej notatki (pod datą i godziną) i jeśli jest większa niż 10 to ustawiam tylko 10 linii + ostatnia stanowiąca wartość ..., żeby nie przekroczyć wysokości komponentu; To samo tyczy się szerokości - jeśli dana linia notatki jest zbyt długa - obcinam ją i dodaję ...; Wielkość jest przydzielana dynamicznie tylko i wyłącznie na podstawie ilości linii notatki i offset'ów przed i po niej; Wysokość jest zawsze taka jaką chcę dla każdych item'ów, tylko po usuwaniu jest problem;

A tak na marginesie, korzystałem z metody AddObject(), ale najpierw przydzielana jest wielkość dla item'ów, a później alokowana jest pamięć dla dodawanego obiektu; Jak sprzężyłem tą metodę z MesaureItem dostawałem zawsze AccessViolation, bo obiekt nie został jeszcze utworzony;

Sama metoda Refresh() (czy Repaint()) nic nie dają; Spróbuje dostać się do odświeżania pojedynczego itemu i spróbować tak jak piszesz; Tyle, że nie mogę tego zrobić dla wybranych, bo usuwanie oparte jest o checkbox'y obok itemów; Tak więc mogę usunąć wiele (multiselect), w tym pierwszy i ostatni; Ale nic nie stoi na przeszkodzie odświeżyć każdy item z osobna po kolei już po usunięciu zaznaczonych;


Nie mam zielonego pojęcia jak odświeżyć pojedynczy item...


Napisałem sobie tak na szybko mały programik wykorzystujący ten styl (w załączniku pliki projektu oraz gotowy exe'k dla tych co nie mają kompilatora) i muszę powiedzieć, że podczas usuwania nie dzieją się takie rzeczy... Błąd gdzieś musi być w moim programie, choć nie sądzę, żebym coś przeskrobał;

Ustalanie wysokości item'a realizowane jest takim algorytmem:

Kopiuj
type
  TNoteLinesArr = array of String;
type
  TNoteInfoRec = record
    DateTime: TDateTime;
    Note: ShortString;
  end;
type
  TNoteInfoArr = array of TNoteInfoRec;

{...}

type
  TNotesListForm = class(TForm)
  {...}
  private
    aNoteInfo: TNoteInfoArr;
  {...}
  end;

procedure TNotesListForm.ExtractNoteLines(Note: ShortString; var LinesArr: TNoteLinesArr);
var
  iLastSepIndex, I: Word;
begin
  case Length(Note) of
    0: Exit;
  else
    Note := Note + #10;
    iLastSepIndex := 1;

    for I := 1 to Length(Note) do
      if Note[I] = #10 then
        begin
          SetLength(LinesArr, Length(LinesArr) + 1);
          LinesArr[High(LinesArr)] := Copy(Note, iLastSepIndex, I - iLastSepIndex);
          iLastSepIndex := I + 1;
        end;
  end;
end;

{...}

procedure TNotesListForm.lbNotesListMeasureItem(Control: TWinControl; Index: Integer; var Height: Integer);
var
  aNoteLines: TNoteLinesArr;
begin
  SetLength(aNoteLines, 0);
  ExtractNoteLines(aNoteInfo[Index].Note, aNoteLines);

  if Length(aNoteLines) > 10 then
    begin
      SetLength(aNoteLines, 11);
      aNoteLines[10] := '...';
    end;

  Height := 48 + Length(aNoteLines) * 15; 
end;

I za pierwszym razem podczas tworzenia itemów rysowanie przebiega bez problemów, ale po usunięciu kilku rysowanie sypie się;


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 5x, ostatnio: flowCRANE
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 11 godzin
  • Lokalizacja:Tuchów
  • Postów:12175
1

Muszę przeprosić, ale rozwiązanie znalazło się...

Wszystko działa bardzo dobrze (poprawnie), ale zapomniałem podczas usuwania item'a z listy także usunąć pole z macierzy aNoteInfo, przez co po usunięciu pierwszego z listy i tak jest jego tekst wyświetlany, który oczywiście nie będzie pasował do przydzielonej powierzchni;

Temat uważam za zamknięty, dziękuję za zainteresowanie;


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 2x, ostatnio: flowCRANE
olesio
Fajnie, że sobie poradziłeś. Może w przyszlości opublikujesz kod tego komponentu lub chociaż program gdzie to użyłeś, bo na moje oko nawet fajnie wygląda :)
flowCRANE
Nie opakowywałem tego w komponent, taka lista będzie unikalna w skali programu więc postanowiłem w module formularza ją obsłużyć; Program jak skończę to opublikuję, piszę go tylko i wyłącznie po to, by utrwalić sobie wszystkie dotychczas zgromadzone informacjeo Delphi; Takie małe podsumowanie obecnego stanu umiejętności :] Tutaj: Delphi prosty explorer przedstawiłem okno dialogowe do wyboru pliku z dysku, także z tego programu;
flowCRANE
Tak przy okazji - jak znajdę więcej czasu to sporządzę artykuł, w którym opiszę w jaki sposób wykorzystać komponent z klasy TCheckListBox do przechowywania programowych informacji dla użytkownika (logów, raportów - jak kto woli) i obsługi stylu "lbOwnerDrawVariable"; No, ale to jeszcze trochę potrwa :P

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.