12

This is probably a stupid question, but my brain is just cooked enough I think I'm going to use one of my "lifelines" to see if I can get some help from my stack overflow friends. ;)

I need to delete all occurrences of a particular component type on my main form (some of them are inside panels or tabsheets, but all on and owned by the same form). Here's what I have now:

for i := 0 to frmMain.ComponentCount - 1 do  
  begin  
    if frmMain.Components[i] is TMyClass then frmMain.Components[i].Destroy;  
  end;    

The problem is (and I knew it would be before I compiled it) that once I destroy the component, the form's component list re-indexes and I end up out of bounds.

What's the best way to solve this? I thought about adding the "found" components to a standalone array, and then walk through that after this loop to delete them, which I think will work.... but is that the best approach?

TIA


UPDATE:

You guys rock. Thanks. : )

Jamo
  • 3,238
  • 6
  • 40
  • 66

6 Answers6

28

You're nearly right. Your loop should look like

for i := frmMain.ComponentCount - 1 downto 0 do
begin
  if frmMain.Components[i] is TMyClass then
    frmMain.Components[i].Free;
end;

This way the call to the function "frmMain.ComponentCount" gets done at the beginning and not again.

You should also call Free as above, not Destroy - I can't remember why at the moment. Bri

mghie
  • 32,028
  • 6
  • 87
  • 129
Brian Frost
  • 13,334
  • 11
  • 80
  • 154
  • 4
    Destroy is virtual. If the object has been destroyed already then it would fail. Free checks to see if it has a valid reference before calling Destroy. Not likely to be a problem here, but a good practice in general. – Jim McKeeth Feb 11 '09 at 23:27
  • 2
    In this case, it is safe to just call Destroy. Because of the way VCL manages this list, there is not likely to be an invalid reference in that list. Even if there were, Free wouldn't protect you because it relies on the instance being nil. – Allen Bauer Feb 12 '09 at 00:34
  • 2
    Note also that the loop goes from high to zero, to ensure all items are considered, otherwise the loop can skip items next to the deleted one. Important not to miss that. – mj2008 Feb 12 '09 at 08:47
  • Another way to get the same effect is to put `frmMain.ComponentCount-1` in a variable. – Fabricio Araujo Apr 15 '16 at 18:43
10

Start at the top and work backwards.

viz:

for i := frmMain.ComponentCount - 1 downto 0 do
begin
  if frmMain.Components[i] is TMyClass then frmMain.Components[i].Free;
end; 

Call free instead of Destroy. Free calls Destroy after checking for a valid reference.

Jim McKeeth
  • 38,225
  • 23
  • 120
  • 194
jrodenhi
  • 2,237
  • 1
  • 21
  • 31
2

It may not happen in your case, but the if frmMain.Components[i] is TMyClass check will also return true for descendant classes of TMyClass. If you are really looking for the removal of one specific class, you may need to add an extra check of the ClassName.

bluish
  • 26,356
  • 27
  • 122
  • 180
pritaeas
  • 2,073
  • 5
  • 34
  • 51
1

The same solution with a while-loop:

i := 0;
while i < frmMain.ComponentCount do
begin
  if frmMain.Components[i] is TMyClass then
    frmMain.Components[i].Free 
  else
    inc(i);
end;
Vegar
  • 12,828
  • 16
  • 85
  • 151
  • this will not work - (i) is going up and (frmMain.ComponentCount) is comming down – Charles Faiga Feb 12 '09 at 16:58
  • 1
    If you take a second look, you will see that i want come up if componentcount goes down. If every single component is of class TMyClass, i would remain at zero, and the while-loop would terminate, because 0 < 0 is false – Vegar Feb 12 '09 at 17:11
-2

For Fine Contrle in form or panel can use this code

var

 i:Integer;

begin

for i := 0 to Panel1.ControlCount - 1 do

  begin

    if Panel1.Controls[i] is TEdit then
       Tedit(Panel1.Controls[i]).text := '';

  end;
Machavity
  • 30,841
  • 27
  • 92
  • 100
-3

If you need to check & destroy a named known component Use

If YourComponent <> Nil Then
  YourComponent.Free;
Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83
  • 2
    This is more ore less the same as `if YourComponent <> nil then if YourComponent <> nil then YourComponent.Destroy;` The double if is not **that** sensible, is it? :-) – Uli Gerhardt Feb 04 '10 at 07:14