how can i make my code to work ? :) i`ve tried to formulate this question but after several failed attempts i think you guys will spot the problem faster looking at the code than reading my 'explanations'. thank you.
setCtrlState([ memo1, edit1, button1], False);
_
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
ct: TClass;
begin
for obj in objs do
begin
ct := obj.ClassType;
if (ct = TMemo) or (ct = TEdit) then
ct( obj ).ReadOnly := not bState; // error here :(
if ct = TButton then
ct( obj ).Enabled:= bState; // and here :(
end;
end;
It would be easier to use RTTI instead of explicit casting, ie:
uses
TypInfo;
setCtrlState([ memo1, edit1, button1], False);
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
PropInfo: PPropInfo;
begin
for obj in objs do
begin
PropInfo := GetPropInfo(obj, 'ReadOnly');
if PropInfo <> nil then SetOrdProp(obj, PropInfo, not bState);
PropInfo := GetPropInfo(obj, 'Enabled');
if PropInfo <> nil then SetOrdProp(obj, PropInfo, bState);
end;
end;
You must explicitly cast object to some class.
This should work:
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
ct: TClass;
begin
for obj in objs do
begin
ct := obj.ClassType;
if ct = TMemo then
TMemo(obj).ReadOnly := not bState
else if ct = TEdit then
TEdit(obj).ReadOnly := not bState
else if ct = TButton then
TButton(obj).Enabled := bState;
end;
end;
This can be shortened using "is
" operator - no need for ct variable:
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
begin
for obj in objs do
begin
if obj is TMemo then
TMemo(obj).ReadOnly := not bState
else if obj is TEdit then
TEdit(obj).ReadOnly := not bState
else if obj is TButton then
TButton(obj).Enabled := bState;
end;
end;
You need to cast the ct object to a TMemo/TEdit/TButton before you can set properties on the object.
The line where you're getting errors are erroring because ct is still a TClass, not a TButton/etc. If you cast to a TButton, then you'll be able to set enabled to true.
I recommend reading up on casting in Delphi. Personally, I would recommend using the as/is operators instead of using ClassType, as well. The code will be simpler in that case, and much more understandable.
Personally, I would write this more like:
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
begin
for obj in objs do
begin
// I believe these could be merged by using an ancestor of TMemo+TEdit (TControl?)
// but I don't have a good delphi reference handy
if (obj is TMemo) then
TMemo(obj).ReadOnly := not bState;
if (obj is TEdit) then
TEdit(obj).ReadOnly := not bState;
if (obj is TButton) then
TButton(obj).Enabled := bState;
end;
end;
There is no need to cast to TMemo and TEdit separately, as they are both descendants from common parent class, which have ReadOnly property:
procedure TForm1.FormCreate(Sender: TObject);
procedure P(const Obj: TComponent);
begin
if Obj is TCustomEdit then
TCustomEdit(Obj).ReadOnly := True;
end;
begin
P(Memo1);
P(Edit1);
end;
You can avoid referencing various units and the explicit casting if you do not mind a small performance hit and limit the changes to published properties. Have a look at the TypInfo unit included with Delphi.