Recent

Author Topic: Free a TPanel within a btn click event handler  (Read 1532 times)

phoenix27

  • Jr. Member
  • **
  • Posts: 92
Free a TPanel within a btn click event handler
« on: August 20, 2023, 07:16:48 am »
At runtime i create a TPanel which contains a TscrollBox and a TButton to close the TPanel itself.
Now, i get an abstract error when i close the TPanel within the TButton click event handler and i sort of understand it, because you can' t remove a button(whose owner is the TPanel) within its click event. A solution would be to make the TPanel a global variable or a field member of its parent and then create a timer in the click event which would remove it without errors. Is there another solution?
« Last Edit: August 20, 2023, 07:20:19 am by phoenix27 »

phoenix27

  • Jr. Member
  • **
  • Posts: 92
Re: Free a TPanel within a btn click event handler
« Reply #1 on: August 20, 2023, 07:37:22 am »
Another thing. If i create a TTimer at runtime in order to remove the TPanel, how do i free it after it has absolved its duty? I cannot do that in the OnTimer event handler, right? It's the same problem as with the TButton click event. Bahh

phoenix27

  • Jr. Member
  • **
  • Posts: 92
Re: Free a TPanel within a btn click event handler
« Reply #2 on: August 20, 2023, 07:44:52 am »
And another thing.(How many ahaha). In an event handler usually there is the sender parameter which you can cast to obtain the real control. How can i store the address of the real control, not the variable? For example:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.ButtonOnClick(Sender: TObject);
  2. var
  3.    pnl: TPanel;
  4. begin
  5.    pnl := Sender as TPanel;
  6.    //let's suppose i have a Pointer ptr somewhere
  7.    ptr := @pnl; // wrong because after the procedure exits that pointer will point to garbage
  8.    //if i do
  9.    ptr := pnl; // i tried but it doesn't work, i thought pnl is a pointer under the hood
  10. end;
  11.  
So how can i get the real address of the panel? Is there a way to get it and store it in a pointer variable?

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Free a TPanel within a btn click event handler
« Reply #3 on: August 20, 2023, 08:41:55 am »
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.   TMyPanel = record
  13.     Panel: TPanel;
  14.     Button: TButton;
  15.     // and whatever else
  16.   end;
  17.   TMyPanels = array of TMyPanel;
  18.  
  19.   { TForm1 }
  20.  
  21.   TForm1 = class(TForm)
  22.     Button1: TButton;
  23.     procedure Button1Click(Sender: TObject);
  24.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  25.   strict private
  26.     Panels: TMyPanels;
  27.   private
  28.     procedure AddPanel(const AOwner: TComponent; const AParent: TWinControl);
  29.     procedure PanelClick(Sender: TObject);
  30.   public
  31.  
  32.   end;
  33.  
  34. var
  35.   Form1: TForm1;
  36.  
  37. implementation
  38.  
  39. {$R *.lfm}
  40.  
  41. { TForm1 }
  42.  
  43. procedure TForm1.Button1Click(Sender: TObject);
  44. begin
  45.   AddPanel(nil, Self);
  46. end;
  47.  
  48. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  49. var
  50.   i: Integer;
  51. begin
  52.   for i := High(Panels) downto Low(Panels) do
  53.     Panels[i].Panel.Free;
  54. end;
  55.  
  56. procedure TForm1.AddPanel(const AOwner: TComponent; const AParent: TWinControl);
  57. var
  58.   i: Integer;
  59. begin
  60.   i := Length(Panels);
  61.   SetLength(Panels, Succ(i));
  62.   Panels[i].Panel := TPanel.Create(AOwner);
  63.   try
  64.     Panels[i].Panel.Parent := AParent;
  65.     Panels[i].Panel.Align := alTop;
  66.     Panels[i].Panel.Height := 25;
  67.     Panels[i].Panel.Tag := i;
  68.     Panels[i].Button := TButton.Create(Panels[i].Panel);
  69.     try
  70.       Panels[i].Button.Parent := Panels[i].Panel;
  71.       Panels[i].Button.Align := alRight;
  72.       Panels[i].Button.Caption := 'Close';
  73.       Panels[i].Button.Width := 80;
  74.       Panels[i].Button.OnClick := @PanelClick;
  75.       Panels[i].Button.Tag := i;
  76.     finally
  77.     end;
  78.   finally
  79.     Panels[i].Panel.Visible := True;
  80.   end;
  81. end;
  82.  
  83. procedure TForm1.PanelClick(Sender: TObject);
  84. var
  85.   i: Integer;
  86. begin
  87.   if Sender is TButton then
  88.     begin
  89.       i := (Sender as TButton).Tag;
  90.       Panels[i].Panel.Free;
  91.       Delete(Panels, i, 1);
  92.     end;
  93. end;
  94.  
  95. end.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

avk

  • Hero Member
  • *****
  • Posts: 773
Re: Free a TPanel within a btn click event handler
« Reply #4 on: August 20, 2023, 10:42:08 am »
This simpler version also seems to work:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     procedure Button1Click(Sender: TObject);
  17.   private
  18.     procedure TryFreeOwner(Sender: TObject);
  19.   public
  20.  
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. { TForm1 }
  31.  
  32. procedure TForm1.TryFreeOwner(Sender: TObject);
  33. begin
  34.   if (Sender is TComponent) and (TComponent(Sender).Owner <> nil) then
  35.     TComponent(Sender).Owner.Free
  36.   else
  37.     Sender.Free;
  38. end;
  39.  
  40. procedure TForm1.Button1Click(Sender: TObject);
  41. var
  42.   Panel: TPanel;
  43.   Button: TButton;
  44. begin
  45.   Panel := TPanel.Create(Self);
  46.   Panel.Left := 5 + Random(10) * 2;
  47.   Panel.Top := 5 + Random(10) * 2;
  48.   Panel.Height := 100;
  49.   Panel.Width := 100;
  50.   Button := TButton.Create(Panel);
  51.   Button.Left := 10;
  52.   Button.Top := 10;
  53.   Button.Caption := 'Close';
  54.   Button.OnClick := @TryFreeOwner;
  55.   Button.Parent := Panel;
  56.   Panel.Parent := Self;
  57. end;
  58.  
  59. end.
  60.  

Bart

  • Hero Member
  • *****
  • Posts: 5573
    • Bart en Mariska's Webstek
Re: Free a TPanel within a btn click event handler
« Reply #5 on: August 20, 2023, 01:08:32 pm »
Can't you use Application.ReleaseComponent(The_panel_in_question) for that?
IIRC the freeing will be queued, so the button on the panel won't be destroyed insid it's own event.

Bart

BrunoK

  • Hero Member
  • *****
  • Posts: 698
  • Retired programmer
Re: Free a TPanel within a btn click event handler
« Reply #6 on: August 20, 2023, 02:45:30 pm »
A technique I used very long ago.

jamie

  • Hero Member
  • *****
  • Posts: 6964
Re: Free a TPanel within a btn click event handler
« Reply #7 on: August 20, 2023, 05:02:00 pm »
Just post a user message to the form.

When you handle that message, you can free the panel from there,

Use POSTMESSAGE
The only true wisdom is knowing you know nothing

cdbc

  • Hero Member
  • *****
  • Posts: 2219
    • http://www.cdbc.dk
Re: Free a TPanel within a btn click event handler
« Reply #8 on: August 20, 2023, 06:34:17 pm »
Hi
In your form, create this message-handler:
Code: Pascal  [Select][+][-]
  1. uses ...,LMessages,LCLIntf;
  2. const
  3.   LMCLOSEPANEL = 1025;
  4.  
  5. TForm1 = class(TForm)
  6. ...
  7. private
  8. ...
  9. public
  10.   procedure LMClosePanel(var Message: TLMessage); message LMCLOSEPANEL;
  11. ...
  12. end;
  13.  
  14. implementation
  15.  
  16. ...
  17. procedure TForm1.LMClosePanel(var Message: TLMessage);
  18. begin
  19.   TPanel(Message.WParam).Free;
  20. end;
  21.  
Then in your TForm1.Button1Click method do this:
Code: Pascal  [Select][+][-]
  1. begin
  2.   postmessage(Form1.Handle,LMCLOSEPANEL,ptrint(myPanel),0);
  3. end;
  4.  
Something like this I have used on occasion, it works.
EDIT: What Jamie said ;)
Another option is Application.AsyncQueue...
HTH
Regards Benny
« Last Edit: August 20, 2023, 06:39:09 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

lainz

  • Hero Member
  • *****
  • Posts: 4723
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: Free a TPanel within a btn click event handler
« Reply #9 on: August 20, 2023, 06:42:49 pm »
Can't you use Application.ReleaseComponent(The_panel_in_question) for that?
IIRC the freeing will be queued, so the button on the panel won't be destroyed insid it's own event.

Bart

+1

phoenix27

  • Jr. Member
  • **
  • Posts: 92
Re: Free a TPanel within a btn click event handler
« Reply #10 on: August 21, 2023, 06:26:52 am »
Identifier idents no member "ReleaseComponent".
Which unit should i include in order to use Application.ReleaseComponent?

Bart

  • Hero Member
  • *****
  • Posts: 5573
    • Bart en Mariska's Webstek
Re: Free a TPanel within a btn click event handler
« Reply #11 on: August 21, 2023, 02:25:30 pm »
TApplication is in Forms unit.

See https://lazarus-ccr.sourceforge.io/docs/lcl/forms/tapplication.releasecomponent.html about it's working mechanism.

Bart

phoenix27

  • Jr. Member
  • **
  • Posts: 92
Re: Free a TPanel within a btn click event handler
« Reply #12 on: August 22, 2023, 04:04:52 am »
Thank you to everybody. Very interesting solutions. I had the unit 'forms' included of course(it's a basic unit after all and gets included automatically) but i couldn't find the method 'ReleaseComponent' and a while ago couldn't find the 'ProcessMessages' method and you know why? Because the unit 'forms' was at the beginning of the 'uses' section and surely the Application class was shadowed by another class with the same name in some other unit declared after it. I solved it by putting 'forms' at the very end.
« Last Edit: August 22, 2023, 04:34:42 am by phoenix27 »

 

TinyPortal © 2005-2018