tags:

views:

228

answers:

4

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.

+2  A: 
(Components[i] as ComponentClass).Visible
Tihauan
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 M
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
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 M
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
@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
@Tihauan: You are of course correct and I don't understand the downvotes either. +1 to make up for them.
mghie
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
@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
This is great, I didn't know this difference between hard and safe type casting.
Harriv
+10  A: 

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
What if you change TControlClass to something else later. Is easy to forget about the dependency you take as granted.
Tihauan
@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
Don't you mean "if Components[i] is TControl"?
David M
@David: See my comment to your answer. It is in `controls.pas`.
mghie
@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.
Ah, ok. Thanks Heinz and mghie! I didn't realise what TControlClass was and I think I was just rather confused.
David M
Of course, I wonder why i didn't think this myself.
Harriv
@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
+1  A: 

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
Are you actually sure all properties `Visible` on all possible Delphi components are of a type that can be hard-casted to `Boolean`?
mghie
@mghie, Good point, I just update the code.
RRUZ
@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.
@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
@mghie: I missed that RRUZ has made the method more general than the original poster needed.
Heinz Z.
Interesting solution, but complicated. Is there ever reason for using this kind of solution?
Harriv
@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
+2  A: 

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