ikathegreat
ikathegreat

Reputation: 2321

Setting an object's property in another procedure

I have an object that has some properties:

Obj.Big
Obj.Rotate
Obj.Paint
Obj.Lines

etc. They are all boolean type properties.

In my main procedure, I call another procedure:

procedure TMainForm.Create(Sender:TObject);
begin
     SetParameter(BigCheckBox, Obj.Big);
     SetParameter(RotateCheckBox, Obj.Rotate);
     SetParameter(PaintCheckBox, Obj.Paint);
     SetParameter(LinesCheckBox, Obj.Lines);
end;

The SetParameter procedure goes like this:

procedure TMainForm.SetParameter(ACheckBox : TCheckBox; ABoolOption : Boolean);
begin
    if(ACheckBox.Checked) and (ACheckBox.Enabled) then begin
      ABoolOption := true;
    end
    else if(not ACheckBox.Checked) and (ACheckBox.Enabled) then begin
      ABoolOption := false;
    end;
end;

It accepts the checkbox object and the property of the passed object's boolean property as ABoolOption. I cannot simple do LinesCheckBox.Checked := Obj.Lines because I need to have a "do nothing" action when the checkbox is filled (they are all tri-state). When I run this, none of those object's parameters are changed. Why is this?

Upvotes: 0

Views: 140

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596497

Your procedure does not do what you think it does. You are NOT passing the property itself, you are passing the CURRENT VALUE of the property instead. If you want to actually update the value of the property, you need to either do what Ken suggests, or use RTTI instead, eg:

uses
  ..., TypInfo;

procedure TMainForm.Create(Sender:TObject);
begin
  SetBooleanParameter(BigCheckBox, Obj, 'Big');
  SetBooleanParameter(RotateCheckBox, Obj, 'Rotate');
  SetBooleanParameter(PaintCheckBox, Obj, 'Paint');
  SetBooleanParameter(LinesCheckBox, Obj, 'Lines');
end;

procedure TMainForm.SetBooleanParameter(ACheckBox : TCheckBox; Obj: TObject; const PropName: String);
begin
  if ACheckBox.Enabled then begin
    // NOTE: this only works if the properties are declared as 'published'
    SetOrdProp(Obj, PropName, Ord(ACheckBox.Checked));
  end;
end;

Or, if you are using D2010+:

uses
  ..., Rtti;

procedure TMainForm.Create(Sender:TObject);
begin
  SetBooleanParameter(BigCheckBox, Obj, 'Big');
  SetBooleanParameter(RotateCheckBox, Obj, 'Rotate');
  SetBooleanParameter(PaintCheckBox, Obj, 'Paint');
  SetBooleanParameter(LinesCheckBox, Obj, 'Lines');
end;

procedure TMainForm.SetBooleanParameter(ACheckBox : TCheckBox; Obj: TObject; const PropName: String);
var
  Ctx: TRttiContext;
begin
  if ACheckBox.Enabled then
  begin
    // NOTE: this approach does not need the properties to be declared as 'published'
    Ctx.GetType(Obj.ClassType).GetProperty(PropName).SetValue(Obj, TValue.From<Boolean>(ACheckBox.Checked));
  end;
end;

Upvotes: 1

Ken White
Ken White

Reputation: 125708

You're not passing the property. You're passing the value of that property. IOW, your SetParameter is receiving ACheckBox, True or ACheckBox, False, and therefore there's nothing for you to change. The better way might be to change your SetParameter procedure to a function:

function TMainForm.SetBooleanValue(const ACheckBox: TCheckBox): Boolean;
begin
  Result := (ACheckBox.Checked) and (ACheckBox.Enabled);
end;

And then use it like:

Obj.Big := SetBooleanValue(BigCheckbox);
Obj.Rotate := SetBooleanValue(RotateCheckBox);
Obj.Paint := SetBooleanValue(PaintCheckBox);
Obj.Lines := SetBooleanValue(LinesCheckBox);

If you need to allow for a third option, you should check it first before performing the call to SetBooleanValue:

if not ThirdCondition then
  Obj.Big := SetBooleanValue(BigCheckBox);

Upvotes: 5

Related Questions