okok
okok

Reputation: 71

I keep getting "Invalid class typecast" error and I don't know why

I am trying to free a component when closing a form but when i run this code I keep getting "Invalid class typecast" error and I don't know why.

procedure TfrmMenu.FormHide(Sender: TObject);
var
  I: Integer;
  sID: String;
begin
  I := 0;
  repeat
    Inc(I);
    with dmMenu.qryMcDonalds do
    begin
      SQL.Text := 'Select ItemID FROM tblMenu WHERE ItemID LIKE "' + IntToStr
        (I) + '%"';
      Open;
    end;
    sID := dmMenu.qryMcDonalds.Fields[0].AsString;

    if (Components[I] as TImage).Name = 'imgd' + sID then
    begin (Components[I] as TImage)
      .Free;
    end;

     if (Components[I] as TLabel).Name = 'lbld' + sID then
      begin (Components[I] as TLabel)
      .Free;
      end;
  until I = ComponentCount - 1;
end;

Upvotes: 3

Views: 7856

Answers (1)

Mason Wheeler
Mason Wheeler

Reputation: 84550

Your problem is in this part:

    if (Components[I] as TImage).Name = 'imgd' + sID then
    begin (Components[I] as TImage)
      .Free;
    end;

     if (Components[I] as TLabel).Name = 'lbld' + sID then
      begin (Components[I] as TLabel)
      .Free;
      end;

An as cast in Delphi is basically an assertion cast: you're saying that you're sure it will be that type, and if it's not, to raise an invalid cast exception. Right now, you're asserting that every single component will be both a TImage and a TLabel, which obviously will never be true.

What you probably really want to do is this:

    if (Components[I] is TImage) and (Components[I].Name = 'imgd' + sID) then
    begin
        Components[I].Free;
    end;

     if (Components[I] is TLabel) and (Components[I].Name = 'lbld' + sID) then
     begin
         Components[I].Free;
      end;

You don't actually need the casts in there, since Name is a property on the TComponent base class.

EDIT: A few points as noted by Andreas in the comments:

First, if you're iterating over a collection and removing items from it, you need to work backwards. To illustrate why, let's remove all even numbers from this list:

[1, 3, 5, 2, 4, 6]

So if we do something like this:

for i := 0 to list.Length - 1 do
begin
   if list[i] mod 2 = 0 then
   begin
      list.Remove(i);
   end;
end;

On the first three iteration, we find numbers that are not even. So far so good. But on #4, (i = 3), we have an even number. We remove it from the list. The list is now [1, 3, 5, 4, 6]. Now we increment i to 4. Element #4 in this new list is the 6; we've skipped the 4! This only works if you're counting backwards, because then deleting things only rearranges the work you've already done, not the work you still have left to do.

Also, the Components collection on the form will contain all of your components, in whatever order they were set up in the form designer. It's not particularly likely that whatever image you called imgd1 will be component #1 in that collection. If you're looking for any components of those types with a name starting with imgd or lbld followed by a number, you'll probably want to write a custom name validation function that checks for that. But checking if the number is the same as I is more or less useless as I is completely arbitrary here.

Upvotes: 4

Related Questions