Recent

Author Topic: [Resolved] TNotifyEvent handler assignment not working as expected  (Read 135 times)

Nimral

  • Sr. Member
  • ****
  • Posts: 253
  • Keep it simple.
What is the thing I am missing in the following sample code:

I do assign DoUpdate := doDisplayValue in FormCreate. Later, when I try to call doDisplayValue, the code in Timer1.Timer does never call doDisplayValue, since assigned(doUpdate) returns false. Seems the assignment does not work, but why?


Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TValueDisplay }
  13.  
  14.   TValueDisplay = class
  15.   private
  16.     FDisplayLabel: TLabel;
  17.   public
  18.     property DisplayLabel: TLabel read FDisplayLabel;
  19.   var
  20.     DoUpdate: TNotifyEvent;                           // called if the value should be updated
  21.     constructor Create(aDisplayLabel: TLabel);        // Bind this display to a certain Label
  22.   end;
  23.  
  24.   TValueDisplays = array of TValueDisplay;            // the list of all value displays
  25.  
  26.   { TForm1 }
  27.  
  28.   TForm1 = class(TForm)
  29.     Label1: TLabel;
  30.     Label2: TLabel;
  31.     Timer1: TTimer;
  32.     procedure FormCreate(Sender: TObject);            // Create the form and register a bunch of displays
  33.     procedure Timer1Timer(Sender: TObject);           // a timer changing a value every 1 second and triggering the update of all registered displays
  34.   private
  35.     FdoDisplayValue: TNotifyEvent;
  36.     FTicks: word;
  37.     FValueDisplays: TValueDisplays;
  38.     procedure SetdoDisplayValue(AValue: TNotifyEvent);
  39.   public
  40.     function AddValueDisplay(aDisplay: TValueDisplay): TValueDisplay;     // register a display
  41.     property ValueDisplays: TValueDisplays read FValueDisplays;           // the list of registered displays
  42.     property doDisplayValue: TNotifyEvent read FdoDisplayValue write SetdoDisplayValue;  // updates the display passed in the sender parameter of TNotifyEvent
  43.     property Ticks: word read FTicks;  // just a changing value
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1;
  48.  
  49. implementation
  50.  
  51. {$R *.lfm}
  52.  
  53. { TValueDisplay }
  54.  
  55. constructor TValueDisplay.Create(aDisplayLabel: TLabel);
  56. begin
  57.   FDisplayLabel := aDisplayLabel;
  58.   FDisplayLabel.Caption := '##';
  59. end;
  60.  
  61. { TForm1 }
  62.  
  63. procedure TForm1.Timer1Timer(Sender: TObject);
  64.  
  65. var
  66.   d: TValueDisplay;
  67.  
  68. begin
  69.   Inc(FTicks);
  70.   for d in ValueDisplays do
  71.     if assigned(d.DoUpdate) then         // if d.DoUpdate <> NIL then
  72.       d.DoUpdate(d.DisplayLabel);
  73. end;
  74.  
  75. procedure TForm1.SetdoDisplayValue(AValue: TNotifyEvent);
  76. begin
  77.   if FdoDisplayValue = AValue then Exit;
  78.   FdoDisplayValue := AValue;
  79. end;
  80.  
  81. function TForm1.AddValueDisplay(aDisplay: TValueDisplay): TValueDisplay;
  82. begin
  83.   SetLength(FValueDisplays, length(FValueDisplays) + 1);
  84.   FValueDisplays[length(FValueDisplays) - 1] := aDisplay;
  85.   Result := FValueDisplays[length(FValueDisplays) - 1];
  86. end;
  87.  
  88. procedure TForm1.FormCreate(Sender: TObject);
  89.  
  90. begin
  91.   with AddValueDisplay(TValueDisplay.Create(Self.Label1)) do
  92.     DoUpdate := doDisplayValue;
  93.   with AddValueDisplay(TValueDisplay.Create(Self.Label2)) do
  94.     DoUpdate := doDisplayValue;
  95. end;
  96.  
  97. end.
  98.  

« Last Edit: August 17, 2022, 03:37:16 pm by Nimral »
Lazarus 2.2.0 on Windows 7,10,11, Debian 10.8 "Buster", macOS Catalina, macOS BigSur, VMWare Workstation 15, Raspberry Pi

Nimral

  • Sr. Member
  • ****
  • Posts: 253
  • Keep it simple.
Re: [Resolved] TNotifyEvent handler assignment not working as expected
« Reply #1 on: August 17, 2022, 03:38:42 pm »
Sorry I have wasted your time, I found the problem. Unfortunately I do not have enough forum permissions to delete the question. DoDisplayValue, which should have been a procedure, was declared as a property, and thus initialized NIL. The assignment worked, but DoDisplayValue was never assigned a value.

This works:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TValueDisplay }
  13.  
  14.   TValueDisplay = class
  15.   private
  16.     FDisplayLabel: TLabel;
  17.   public
  18.     property DisplayLabel: TLabel read FDisplayLabel;
  19.   var
  20.     DoUpdate: TNotifyEvent;                           // called if the value should be updated
  21.     constructor Create(aDisplayLabel: TLabel);        // Bind this display to a certain Label
  22.   end;
  23.  
  24.   TValueDisplays = array of TValueDisplay;            // the list of all value displays
  25.  
  26.   { TForm1 }
  27.  
  28.   TForm1 = class(TForm)
  29.     Label1: TLabel;
  30.     Label2: TLabel;
  31.     Timer1: TTimer;
  32.     procedure FormCreate(Sender: TObject);            // Create the form and register a bunch of displays
  33.     procedure Timer1Timer(Sender: TObject);           // a timer changing a value every 1 second and triggering the update of all registered displays
  34.   private
  35.     FTicks: word;
  36.     FValueDisplays: TValueDisplays;
  37.   public
  38.     function AddValueDisplay(aDisplay: TValueDisplay): TValueDisplay;     // register a display
  39.     property ValueDisplays: TValueDisplays read FValueDisplays;           // the list of registered displays
  40.     procedure doDisplayValue(Sender:TObject);  // updates the display passed in the sender parameter of TNotifyEvent
  41.     property Ticks: word read FTicks;  // just a changing value
  42.   end;
  43.  
  44. var
  45.   Form1: TForm1;
  46.  
  47. implementation
  48.  
  49. {$R *.lfm}
  50.  
  51. { TValueDisplay }
  52.  
  53. constructor TValueDisplay.Create(aDisplayLabel: TLabel);
  54. begin
  55.   FDisplayLabel := aDisplayLabel;
  56.   FDisplayLabel.Caption := '##';
  57. end;
  58.  
  59. { TForm1 }
  60.  
  61. procedure TForm1.Timer1Timer(Sender: TObject);
  62.  
  63. var
  64.   d: TValueDisplay;
  65.  
  66. begin
  67.   Inc(FTicks);
  68.   for d in ValueDisplays do
  69.     if assigned(d.DoUpdate) then         // if d.DoUpdate <> NIL then
  70.       d.DoUpdate(d.DisplayLabel);
  71. end;
  72.  
  73. function TForm1.AddValueDisplay(aDisplay: TValueDisplay): TValueDisplay;
  74. begin
  75.   // aDisplay.DoUpdate := doDisplayValue;
  76.   SetLength(FValueDisplays, length(FValueDisplays) + 1);
  77.   FValueDisplays[length(FValueDisplays) - 1] := aDisplay;
  78.   // FValueDisplays[length(FValueDisplays) - 1].DoUpdate := doDisplayValue;
  79.   Result := FValueDisplays[length(FValueDisplays) - 1];
  80. end;
  81.  
  82. procedure TForm1.doDisplayValue(Sender: TObject);
  83.  
  84. begin
  85.    With TLabel(Sender) do
  86.      Caption := Format('%d',[Ticks]);
  87. end;
  88.  
  89. procedure TForm1.FormCreate(Sender: TObject);
  90.  
  91. begin
  92.   with AddValueDisplay(TValueDisplay.Create(Self.Label1)) do
  93.     DoUpdate := @doDisplayValue;
  94.   with AddValueDisplay(TValueDisplay.Create(Self.Label2)) do
  95.     DoUpdate := @doDisplayValue;
  96. end;
  97.  
  98. end.
  99.  
« Last Edit: August 17, 2022, 03:43:04 pm by Nimral »
Lazarus 2.2.0 on Windows 7,10,11, Debian 10.8 "Buster", macOS Catalina, macOS BigSur, VMWare Workstation 15, Raspberry Pi

 

TinyPortal © 2005-2018