FindComponent - usuwanie i "Argument out of range"

0

Pozwolę sobie założyć nowy wątek, bo lektura forum nie przyniosła odpowiedzi.
Otóż mam program (galerię obrazów) w którym chciałbym dodać możliwość usuwania wybranych obrazów z widoku. Obrazy są dodawane jako obiekty TImage z przypisanym do nich checkboxem. Poniżej kod procedury usuwania:

 procedure TForm1.Button5Click(Sender: TObject);
var
  J : Integer;
  S : String;
  NewStr, OldStr : String;
begin
  for J:=0 to componentcount-1 do
    begin
     if (Components[J] is Tcheckbox) and ((Components[J] as Tcheckbox).IsChecked=true)then
          begin
        S:= (components[J].Name);
        oldstr:='czek';
        newstr:='glowny';
        S := StringReplace(S, OldStr, NewStr, [rfReplaceAll]);
      findcomponent(s).free;

           end;
    end;
end;

Kiedy obrazów jest dużo wszystko działa bez zarzutu, ale kiedy schodzę do 3-5 to za każdym razem wyskakuje mi błąd:
"Argument out of range". Mam chyba znaczący brak wiedzy, bo nie mogę tego przeskoczyć.

0

Musiałbyś zrobić pętlę While i inkrementować tylko wtedy, gdy nie zwalniasz komponentu.

0

Kasuj od końca:

for J:=ComponentCount-1 downto 0 do
0
findcomponent(s).free

zmniejsza liczbę komponentów i wymiar tablicy Components, tym samym odwołanie Components[J] może wykraczać poza jej rozmiar

0

albo pętlę od componentcount - 1 do 0

0

Kasując od końca też wywala "Argument out of range". Jestem mocno zdziwiony bo czasami wywala błąd przy 3 ostatnich, czasami przy ostatnim. Jak jest dużo komponentów to się ładnie usuwają, ale przy końcówce zawsze daje błąd. Jest jakaś metoda na wyszukiwanie komponentów wg typu? Tzn. chciałbym znaleźć wszystkie komponenty typu TCheckbox bez znajomości ich nazwy.

0

bo 'componentcount' zlicza wszystkie komponenty, w tym również checkboxy

0

utwórz metodę która będzie kasowała tylko jeden (pierwszy znaleziony) komponent z zaznaczonych do skasowania i zwróci wartość logiczną True jeśli coś skasowano i False jeśli już nic nie było do skasowania

a potem zrób coś takiego :

while self.kasuj do ;

pętla będzie się wykonywać aż do usunięcia ostatniego komponentu

0

Poszedłem w stronę tego

 while

, co prawda błąd zniknął ale teraz kasuje mi na raty. Tzn. mam zaznaczone np 9 elementów, najpierw skasuje 5, potem 3 i na końcu jeden. Trochę kodu:

 begin

J := 0;
while J < componentcount - 1 do
    begin
     if (Components[J] is Tcheckbox) and ((Components[J] as Tcheckbox).IsChecked=true)then
          begin
        S:= (components[J].Name);
        oldstr:='czek';
        newstr:='glowny';
        S := StringReplace(S, OldStr, NewStr, [rfReplaceAll]);
        findcomponent(s).Free;
          end;
       J:=J+1;
          end;
end;
0

Wariant drugi , to tworzysz dynamiczną tablicę zawierającą wskaźniki do komponentów które mają być usunięte.
W pierwszej pętli tylko wyszukujesz komponenty do skasowania i dodajesz do tablicy odpowiednie wskaźniki, a w kolejnej lecisz po tej tablicy i kasujesz

0
var
  I: Integer;
  a: array of tcomponent;
begin
  setlength(a, 0);
  for I := 0 to self.Componentcount - 1 do
    if { tu warunek kasowania }  then
    begin
      setlength(a, length(a) + 1);
      a[ high(a)] := self.Components[I];
    end;
  for I := 0 to high(a) do
    a[I].free;
end;
0

Dostałeś odpowiedź w drugim poście ale nadal robisz to samo, masz gotowe poprawne rozwiązanie:

for J:=ComponentCount-1 downto 0 do
  if (Components[J] is Tcheckbox)and((Components[J] as Tcheckbox).IsChecked) then
    findcomponent(StringReplace(components[J].Name,'czek','glowny',[])).Free;
0

Nie jest to poprawne rozwiązanie, niestety. Taki kod zwraca "Argument out of range". Wersja z odliczaniem w dół to pierwsza którą napisałem.

1
J:=ComponentCount-1;
while J>=0 do
begin
  if (Components[J] is Tcheckbox)and((Components[J] as Tcheckbox).IsChecked) then
  begin
    findcomponent(StringReplace(components[J].Name,'czek','glowny',[])).Free;
    if J>ComponentCount then J:=ComponentCount;
  end;
  Dec(J);  
end;
0

Dobra ciemny jestem, i nie umiem programować:). Sypię głowę popiołem. _13th_Dragon dzięki wielkie, ten kod działa poprawnie i szybko.
Dzięki wszystkim za pomoc

0

Z tym że powinieneś to przerobić na normalne tablice.

0
```delphi J := 0;

while J < ComponentsCount do
begin
if (Components[J] is TCheckBox) and (TCheckBox(Components[J]).IsChecked) then
begin
findcomponent(StringReplace(components[J].Name,'czek','glowny',[])).Free;
end else
begin
Inc(J);
end;
end;

Ale pisanie nieskomplikowanego kodu jest takie mainstreamowe, wiem ;p</del>

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