Recent

Author Topic: [SOLVED] Can't access component property  (Read 1332 times)

pcurtis

  • Hero Member
  • *****
  • Posts: 951
[SOLVED] Can't access component property
« on: May 13, 2021, 09:00:39 am »
I am trying to design a component. When I try to set a property at runtime it fails with "unit1.pas(40,26) Error: Argument cannot be assigned to".
What's wrong?

Code: Pascal  [Select][+][-]
  1. unit mythreaddisplay;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, LResources, Forms, Controls, Graphics, StdCtrls, ExtCtrls, ComCtrls, Dialogs, Buttons, Windows;
  9. type
  10.   TThreadInfo = record
  11.     Position : Integer;
  12.     Caption : String;
  13.   end;
  14.  
  15.   TMyThreadDisplay = class(TCustomControl)
  16.   private
  17.     flblLabel : array[0..4] of TLabel;
  18.     fprbProgressbar : array[0..4] of TProgressBar;
  19.     fThreadInfo : array[0..4] of TThreadInfo;
  20.     function GetThreadInfo(Index: Integer): TThreadInfo;
  21.     procedure SetThreadInfo(Index: Integer; AValue: TThreadInfo);
  22.   protected
  23.  
  24.   public
  25.     constructor Create(AOwner : TComponent); override;
  26.     property ThreadInfo[Index: Integer]: TThreadInfo read GetThreadInfo write SetThreadInfo;
  27.   published
  28.     property Align;
  29.   end;
  30.  
  31. procedure Register;
  32.  
  33. implementation
  34.  
  35. function TMyThreadDisplay.GetThreadInfo(Index: Integer): TThreadInfo;
  36. begin
  37.   Result := fThreadInfo[Index];
  38. end;
  39.  
  40. procedure TMyThreadDisplay.SetThreadInfo(Index: Integer; AValue: TThreadInfo);
  41. begin
  42.   if fThreadInfo[Index].Caption <> AValue.Caption then
  43.     begin
  44.       fThreadInfo[Index].Caption := AValue.Caption;
  45.       flblLabel[Index].Caption := AValue.Caption;
  46.     end;
  47.  
  48.   if fThreadInfo[Index].Position <> AValue.Position then
  49.     begin
  50.       fThreadInfo[Index].Position := AValue.Position;
  51.       fprbProgressbar[Index].Position := AValue.Position;
  52.     end;
  53. end;
  54.  
  55. constructor TMyThreadDisplay.Create(AOwner : TComponent);
  56. var
  57.   iTEMP : Integer;
  58. begin
  59.   inherited Create(AOwner);
  60.  
  61.   for iTEMP := 4 downto 0 do
  62.     begin
  63.       fprbProgressbar[iTEMP] := TProgressBar.Create(Self);
  64.  
  65.       with fprbProgressbar[iTEMP] do
  66.         begin
  67.           fThreadInfo[iTEMP].Position := 0;
  68.           Align := alTop;
  69.           Parent := Self;
  70.           Position := fThreadInfo[iTEMP].Position;
  71.         end;
  72.  
  73.       flblLabel[iTEMP] := TLabel.Create(Self);
  74.         with flblLabel[iTEMP] do
  75.           begin
  76.             fThreadInfo[iTEMP].Caption := 'Thread ' + iTEMP.ToString;
  77.             Caption := fThreadInfo[iTEMP].Caption;
  78.             Align := alTop;
  79.             Parent := Self;
  80.             ParentFont := True;
  81.           end;
  82.       end;
  83.  
  84.   Constraints.MinHeight := 205;
  85.   Constraints.MinWidth := 100;
  86.   SetBounds(0, 0, 100, 205);
  87. end;
  88.  
  89. procedure Register;
  90. begin
  91.   RegisterComponents('Misc', [TMyThreadDisplay]);
  92. end;
  93.  
  94. end.
  95.  

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, StdCtrls,
  9.   mythreaddisplay;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     procedure FormCreate(Sender: TObject);
  17.   private
  18.  
  19.   public
  20.  
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25.   MyDisplay : TMyThreadDisplay;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.FormCreate(Sender: TObject);
  34. begin
  35.   MyDisplay := TMyThreadDisplay.Create(self);
  36.   MyDisplay.Left := 8;
  37.   MyDisplay.Top := 8;
  38.   MyDisplay.Parent := Form1;
  39.  
  40.   MyDisplay.ThreadInfo[1].Caption := 'something';  // fails here
  41. end;
  42.  
  43. end.
  44.  
  45.  
« Last Edit: May 13, 2021, 11:44:51 am by pcurtis »
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Can't access component property
« Reply #1 on: May 13, 2021, 11:07:47 am »
If you have a property that is of a record type you must change it as a whole. This avoids people wondering why their code does not work, because the getter only provides a copy (*).

So you need to use this:

Code: Pascal  [Select][+][-]
  1. var
  2.   info: TThreadInfo;
  3. begin
  4.   info := MyDisplay.ThreadInfo[1];
  5.   info.Caption := 'something';
  6.   MyDisplay.ThreadInfo[1] := info;
  7. end;

Alternatively you can use a class instead of a record for TThreadInfo (you need to instantiate them correctly of course). Or your property needs to a pointer to the TThreadInfo.

(*) That is because the compiler would essentially generate code like this:

Code: Pascal  [Select][+][-]
  1. var
  2.   tmp: TThreadInfo;
  3. begin
  4.   // MyDisplay.ThreadInfo[1].Caption := 'something';
  5.   tmp := MyDisplay.ThreadInfo[1];
  6.   tmp.Caption := 'something'

The tmp record would be a copy, not a reference.

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Can't access component property
« Reply #2 on: May 13, 2021, 11:42:16 am »
Thank you.
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

 

TinyPortal © 2005-2018