2

How to do this in Delphi:

procedure ToggleVisibility(ControlClass : TControlClass);
var
  i : integer;
begin
  for i := 0 to ComponentCount - 1 do
    if Components[i] is ControlClass then
      ControlClass(Components[i]).Visible := not Control(Components[i]).Visible;
end;

Compiler doesn't allow the cast in this case. Any ideas?

I'm using Delphi 2007.

Harriv
  • 6,029
  • 6
  • 44
  • 76
  • Possible duplicate of http://stackoverflow.com/questions/1083087/cast-tobject-using-his-classtype - I'm not sure because I'm not 100% certain I understand what you're trying to do. – David Nov 12 '09 at 12:54
  • 2
    why do you call the parameter ComponentClass, but it is of type TControlClass? Shouldn't the parameter be named ControlClass in stead? – Jeroen Wiert Pluimers Nov 12 '09 at 14:57
  • 1
    @Harriv: An edit like that is somewhat unfortunate, as it's hard to tell later why the answers are using a different (worse) naming scheme. I completely agree with Jeroen (and if you do too you should upvote his comment too), but this edit should maybe be rolled back. You also introduced another error. – mghie Nov 13 '09 at 12:11

4 Answers4

10

Since the component is a TControl or a descendant you have to cast to TControl:

procedure ToggleVisibility(ComponentClass : TControlClass);
var
  i : integer;
begin
  for i := 0 to ComponentCount - 1 do begin
    if Components[i] is ComponentClass then
      TControl(Components[i]).Visible := not TControl(Components[i]).Visible;
  end;
end;
mghie
  • 32,028
  • 6
  • 87
  • 129
  • What if you change TControlClass to something else later. Is easy to forget about the dependency you take as granted. – Tihauan Nov 12 '09 at 12:47
  • @Tihauan: True. I didn't think too much about it, as I consider the whole idea flawed and would never write such a procedure. Note that it doesn't even compile as it stands, same as the code in the question. – mghie Nov 12 '09 at 12:52
  • Don't you mean "if Components[i] is TControl"? – David Nov 12 '09 at 12:53
  • @David: See my comment to your answer. It is in `controls.pas`. – mghie Nov 12 '09 at 12:54
  • 1
    @David The `Components[i] is ComponentClass` check wether Componets[i] is of a class which the method should handle. `TControl(Components[i])` is the way to access the `Visible` property. And that is right, because each instance of a class which is compatible to TControlClass is a descendant of TControl. To make the code better reading the var `ComponentClass` should rename to `ControlClass`. – Heinz Z. Nov 12 '09 at 13:21
  • Ah, ok. Thanks Heinz and mghie! I didn't realise what TControlClass was and I think I was just rather confused. – David Nov 12 '09 at 13:47
  • @Tihauan: It doesn't matter whether you're going to change TControlClass to something else. What this method is dealing with is the `Visible` property and that is introduced in `TControl`. – Oliver Giesen Nov 16 '09 at 09:00
2
(Components[i] as ComponentClass).Visible
Tihauan
  • 2,780
  • 2
  • 26
  • 29
  • If I understand the question properly, this won't work because ComponentClass is a metaclass, and you can't cast using one of those. I replied with more explanation - just thought I should explain the downvote. – David Nov 12 '09 at 12:51
  • 2
    I appreciate the explanation. It is actually possible to use class types for safe casting. You can try the code if you don't believe me. – Tihauan Nov 12 '09 at 12:54
  • I don't have Delphi where I am right now, but I'll take your word for it! I just tried to undo the downvote and it said it was too old - I'm sorry :/ – David Nov 12 '09 at 13:06
  • hmm, up to us to upvote it then. Though.. I have Delphi at hand, and 'TComponent' doesn't have the Visible property, so I'm afraid my upvote will go to mghie's post. – Stijn Sanders Nov 12 '09 at 13:36
  • @Stijn: TComponent doesn't have the Visible property BUT looking closely you can see the component is casted to "ComponentClass" which is a TControlClass type, which actually has the Visible property. I thought it shouldn't be that hard to figure out that the my solution WORKS and answers the question. – Tihauan Nov 12 '09 at 14:05
  • 1
    @Tihauan: You are of course correct and I don't understand the downvotes either. +1 to make up for them. – mghie Nov 12 '09 at 14:43
  • an interesting but overlooked point is the fact that Tihauan uses "AS" to cast. IMO It is more elegant than checking with "IS" and explicitly typecasting. – PA. Nov 12 '09 at 14:43
  • @Pa: You can't skip the `is` though without changing the workings of the code, so you end up with `is` **and** `as`. I don't know whether that's really more elegant... – mghie Nov 12 '09 at 14:46
  • This is great, I didn't know this difference between hard and safe type casting. – Harriv Nov 13 '09 at 12:07
2

It does not make sense to cast ComponentClass(Components[i]).Visible, because .Visible needs to be of a specific class, in order to be compiled properly. Therefore, you need to specify the exact class that should be cast to. For instance, if TControl has a .Visible property, but a derived class creates a new kind of .Visible property, the compiler would not know, which of these two properties it should compile for.

So the question is, do you want to invert the TControl.Visible, then you should write (Components[i] as TControl).Visible. I guess that's what you want.

If you want to invert the .Visible of any TControl descendent, no matter if it relates to the control being Visible or not, and no matter if it is related to TControl.Visible or not, then you should go for the RTTI solution described elsewhere.

Lars D
  • 8,483
  • 7
  • 34
  • 37
1

Try this option using the RTTI

Uses
 TypInfo;

procedure TForm1.ToggleVisibility(ComponentClass: TClass);
var
  i       : integer;
  PropInfo: PPropInfo;
  aValue  : Variant;
begin
  for i := 0 to ComponentCount - 1 do
    if Components[i] is ComponentClass then
     begin
      PropInfo := GetPropInfo(Components[i].ClassInfo, 'Visible');
      if Assigned(PropInfo) then
      begin
       aValue:=GetPropValue(Components[i], 'Visible');
       if PropInfo.PropType^.Kind=tkEnumeration then //All enumerated types. This includes Boolean, ByteBool, WordBool, LongBool and Bool
       SetOrdProp(Components[i], PropInfo, Longint(not Boolean(aValue)));
      end;
     end;
end;

To execute

ToggleVisibility(TEdit);
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • 1
    Are you actually sure all properties `Visible` on all possible Delphi components are of a type that can be hard-casted to `Boolean`? – mghie Nov 12 '09 at 13:06
  • @mghie To change the type of a property you need to redeclare the property. But with a redeclaration of property Visible you can avid hiding/showing the control, so that your code maybe also don't work like expected. :-) – Heinz Z. Nov 12 '09 at 13:33
  • @Heinz: That may be true, but doesn't matter for above code. Note that the method takes an argument of any class, so the passed instance need not be a control and thus may have a completely different `Visible` property. Since there are no type constraints one should not assume anything about it in the code. – mghie Nov 12 '09 at 14:40
  • @mghie: I missed that RRUZ has made the method more general than the original poster needed. – Heinz Z. Nov 12 '09 at 16:35
  • Interesting solution, but complicated. Is there ever reason for using this kind of solution? – Harriv Nov 13 '09 at 12:09
  • @Harriv: Yes there is, as there is no duck typing in Delphi. Consider two distinct classes that are not in an ancestor - descendant relationship but both have a property with the same name and type. This solution would cater for it. – mghie Nov 13 '09 at 12:16