0

So, I'm trying to make a component that will do the job on setting the settings of a excel, libreoffice, etc... cells. At first I just wanted to set the value, but now, I need to change cell background color, change font name, style, set a formula, etc... So for that, I decided to do a type that will hold all the things I want to change and so, I did this:

type
  TMyCell = class
private
  FBgColor: TColor;
  FValue: String;
  FFormula: String;
  FFormat: String;
  FFont: TFont;

public
  constructor Create;
  destructor Destroy;

  property Value: String read FValue write FValue;
  property Formula: String read FFormula write FFormula;
  property Format: String read FFormat write FFormat;
  property BgColor: TColor read FBgColor write FBgColor;
  property Font: TFont read FFont write FFont;

end;

{ TMyCell }

constructor TMyCell.Create;
begin
  FFont := TFont.Create;
end;

destructor TMyCell.Destroy;
begin
  FFont.Free;
end;

And my component look like this:

type
  TMyPlan = class(TComponent)
private
  FExcel: Variant;
  procedure SetMyCell(Row, Column: Integer; Value: TMyCell);
  function GetMyCell(Row, Column: Integer): TMyCell;

public
  constructor Create(AOwner: TComponent);
  destructor Destroy;

  property Cell[Row, Column: Integer]: TMyCell read GetMyCell write SetMyCell;
end;

{ TMyPlan }

constructor TMyPlan.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FExcel := CreateOleObject('Excel.Application');
  FExcel.Workbooks.Add(1);
end;

destructor TMyPlan.Destroy;
begin
  FExcel := Unassigned;
  inherited;
end;

function TMyPlan.GetMyCell(Row, Column: Integer): TMyCell;
begin
  Result := TMyCell.Create;
  Result.Value := FExcel.Cells[Row, Column];;
end;

procedure TMyPlan.SetMyCell(Row, Column: Integer; Value: TMyCell);
begin
  FExcel.Cells[Row, Column] := Value.Value;
end;

Just to let you know, I already did some components, and I'm still learning how to do them properly, so this may have a not decent structure, anyway, this is the first time that I'm trying to do something like this, a property that has input parameters with subproperties, and it doesn't seem to work as I though it would.

Back to the topic, it doesn't matter how I call my property

Set: MyPlan.Cell[1, 1].Value := '1';

Get: ShowMessage(MyPlan.Cell[1, 1].Value);

Either way only the GetMyCell function is triggered. Why's that?

Ken White
  • 123,280
  • 14
  • 225
  • 444

1 Answers1

3

See my answer to this question: "Left side cannot be assigned to" for record type properties in Delphi

While what you're doing isn't quite the same thing, it is similar. However, in your case, you're allocating a new instance of TMyCell for every access to GetMyCell. This "temporary" instance is isn't being freed and will leak (Unless you're doing this on one of the ARC platforms).

The reason your SetMyCell isn't being called is because you're not actually setting the cell itself, rather you're setting a value on the cell instance (that I explained above is leaking).

Community
  • 1
  • 1
Allen Bauer
  • 16,657
  • 2
  • 56
  • 74
  • I understood what you said on this answer and the answer on the other question, but I can't relate my situation with the other one, because in the record one, you're using a write directly on the record variable, but in my case I need to have a write in a procedure. Sorry if that's not really the point you wanted me to get, but I can't see any thing more than that. – Geovane Silveira Apr 19 '16 at 15:35
  • 1
    There are several distinct operations within the MyPlan.Cell[1, 1].Value := '1'; statement. MyPlan.Cell[1,1] must *read* the cell (aka. call GetMyCell) before it can apply the '.' operator. That value is placed into a local temporary variable by the compiler. The ".Value" portion operates only on that temporary and at no point will SetMyCell ever be called. – Allen Bauer Apr 19 '16 at 15:38