Recent

Author Topic: [solved]Dynamically adding and freeing controls  (Read 1440 times)

indydev

  • Full Member
  • ***
  • Posts: 109
[solved]Dynamically adding and freeing controls
« on: June 22, 2024, 07:13:18 am »
I am using a scrollbox to dynamically add controls and then remove them (when the user wishes to do so).  I can't seem to free the controls without triggering a SIGSEV. I can use RemoveControl(controls[n]) but does that free the control?  The user could clear and add, and clear and add for a very long time. If RemoveControl does not free the controls I would be piling up memory wouldn't I. Here is what I have now:

Code: Pascal  [Select][+][-]
  1. for i := ControlCount - 1 downto 0 do
  2.   begin
  3.     Control := Controls[i];
  4.     RemoveControl(Controls[i]);
  5.     Control.Free;  //causes SIGSEV. If I comment out, no problem.
  6.   end;    
« Last Edit: June 23, 2024, 03:28:03 pm by indydev »

TRon

  • Hero Member
  • *****
  • Posts: 2878
Re: Dynamically adding and freeing controls
« Reply #1 on: June 22, 2024, 07:20:11 am »
If RemoveControl does not free the controls I would be piling up memory wouldn't I.
Depends on how you create the control(s). If you have set the owner then it is the owner's responsibility to free the control(s).

Oh, almost forgot. When you have added the controls correctly ...
Code: Pascal  [Select][+][-]
  1.  RemoveControl(Controls[i]);
... then please do not do that.
« Last Edit: June 22, 2024, 07:37:01 am by TRon »

indydev

  • Full Member
  • ***
  • Posts: 109
Re: Dynamically adding and freeing controls
« Reply #2 on: June 22, 2024, 07:56:18 am »
Quote
Depends on how you create the control(s). If you have set the owner then it is the owner's responsibility to free the control(s).

The code I posted is the owner exercising that responsibility.  :)

I want to keep the owner.

Maybe there wasn't enough context. This is a scrollbox helper:

Code: Pascal  [Select][+][-]
  1. unit ScrollBoxHelper;
  2.  
  3. interface
  4.  
  5. uses
  6.   Classes, Controls, Forms;
  7.  
  8. type
  9.   TScrollBoxHelper = class helper for TScrollBox
  10.   public
  11.     procedure Clear;
  12.   end;
  13.  
  14. implementation
  15.  
  16. procedure TScrollBoxHelper.Clear;
  17. var
  18.   i: Integer;
  19.   Control: TControl;
  20. begin
  21.   for i := ControlCount - 1 downto 0 do
  22.   begin
  23.     Control := Controls[i];
  24.     RemoveControl(Controls[i]);
  25.     Control.Free;
  26.   end;
  27. end;
  28.  
  29. end.
  30.  

The controls are added this way:
Code: Pascal  [Select][+][-]
  1. constructor TDiscussionPanel.Create(AOwner: TComponent; DText, KyWord: string);
  2. const
  3.   border = 8;
  4. begin
  5.   inherited Create(AOwner);
  6.   Self.Parent := AOwner as TWinControl;
  7.   Self.OnResize:=@Resize;
  8.   Self.OnClick:=@SelfClick;
  9.   Self.OnMouseMove:=@SelfMouseMove;
  10.   Self.OnMouseDown:=@SelfMouseDown;
  11.   Self.OnMouseUp:=@SelfMouseUp;
  12. end;
  13.  

And is called this way:

Code: Pascal  [Select][+][-]
  1. newDiscussionPanel := TDiscussionPanel.Create(Scrollbox1, txt, kword);
  2.  

Is this wrong?

Everything about these dynamically created controls works as intended ...except their removal.

TRon

  • Hero Member
  • *****
  • Posts: 2878
Re: Dynamically adding and freeing controls
« Reply #3 on: June 22, 2024, 08:03:31 am »
Ok, thank you for the lot of your helper project.

Everything about these dynamically created controls works as intended ...except their removal.
simply remove ...
Code: [Select]
RemoveControl(Controls[i]);
... from your Clear helper method. The owner will take care of that for you when freeing the control(s).

egsuh

  • Hero Member
  • *****
  • Posts: 1394
Re: Dynamically adding and freeing controls
« Reply #4 on: June 22, 2024, 08:24:17 am »
I think the OP's intention might be to create and remove controls, depending on actions of "users" of the application.

In that case, you might have to
 -  create TDiscussionPanel with Owner=nil and Parent = ScrollBox1.
 - and call RemoveControl (probably without Freeing it, if RemoveControl frees the controls).


indydev

  • Full Member
  • ***
  • Posts: 109
Re: Dynamically adding and freeing controls
« Reply #5 on: June 22, 2024, 08:41:38 am »
Indeed, the concern is developing a memory leak while the user adds and removes controls.  By clearing them with my .clear method.
« Last Edit: June 22, 2024, 08:44:08 am by indydev »

jamie

  • Hero Member
  • *****
  • Posts: 6370
Re: Dynamically adding and freeing controls
« Reply #6 on: June 22, 2024, 04:30:51 pm »
Just call free on the control, the control itself handles the rest.

Also, you may have other items in the control that needs to notify others of what is happening, losing a link to the owner or parent while this is happening is not such a good idea.


 So enumerate backwards as you are and call free on it, you don't even need to define a local control, you can take it out of the array of controls directly.

Code: Pascal  [Select][+][-]
  1.  For I := ControlCount-1 Downto 0 to Controls[I].Free;
  2.  


 Unless I missed something, that should work.
The only true wisdom is knowing you know nothing

indydev

  • Full Member
  • ***
  • Posts: 109
Re: Dynamically adding and freeing controls
« Reply #7 on: June 22, 2024, 08:00:01 pm »
That is what I did at first. This is what causes the error. It bugged me for several days, until I found RemoveControl.

But this is the code for RemoveControl: (found in wincontrol.inc)

Code: Pascal  [Select][+][-]
  1. procedure TWinControl.RemoveControl(AControl: TControl);
  2. var
  3.   AWinControl: TWinControl;
  4.   AGrControl: TGraphicControl;
  5. begin
  6.   DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.RemoveControl'){$ENDIF};
  7.   try
  8.     Perform(CM_CONTROLCHANGE, WParam(AControl), LParam(False));
  9.     if AControl is TWinControl then
  10.     begin
  11.       AWinControl := TWinControl(AControl);
  12.       AWinControl.RemoveFocus(True);
  13.       if AWinControl.HandleAllocated then
  14.         AWinControl.DestroyHandle;
  15.     end
  16.     else
  17.     begin
  18.       if AControl is TGraphicControl then
  19.       begin
  20.         AGrControl := TGraphicControl(AControl);
  21.         if (AGrControl.Canvas<>nil) then
  22.           TControlCanvas(AGrControl.Canvas).FreeHandle;
  23.       end;
  24.       if HandleAllocated then
  25.         AControl.InvalidateControl(AControl.IsVisible, False, True);
  26.     end;
  27.     Remove(AControl);
  28.     Perform(CM_CONTROLLISTCHANGE, WParam(AControl), LParam(False));
  29.     if not (csDestroying in ComponentState) then
  30.     begin
  31.       InvalidatePreferredSize;
  32.       AdjustSize;
  33.     end;
  34.   finally
  35.     EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.RemoveControl'){$ENDIF};
  36.   end;
  37. end;
  38.  

It frees handles, but I don't see any calls to Free, FreeAndNil, or Destroy; but I am pretty far beyond my understanding of what the builders of the LCL are doing here.

The TControls in the TScrollbox (customized TPanels, called TDiscussionPanel) have TControls as private fields of the customized TPanels. I free them in the  .Destroy method of TDiscussionPanel.

Code: Pascal  [Select][+][-]
  1. destructor TDiscussionPanel.Destroy;
  2. begin
  3.   FAgentPic.Free;    // TBitmap
  4.   FCopyPic.Free;     // TBitmap
  5.   FDialogue.Free;    // TRichMemo
  6.   FCodeName.Free;    // TLabel
  7.   FCodeCopy.Free;    // TLabel
  8.   inherited Destroy;
  9. end;

jamie

  • Hero Member
  • *****
  • Posts: 6370
Re: Dynamically adding and freeing controls
« Reply #8 on: June 22, 2024, 11:24:52 pm »
You do not need to Nil the reference since it's already gone from the array of controls after you call free on it.

The releasing of a control is pretty much taking care of when you free it. ownership and parenting are managed for you.

The only true wisdom is knowing you know nothing

indydev

  • Full Member
  • ***
  • Posts: 109
Re: Dynamically adding and freeing controls
« Reply #9 on: June 23, 2024, 02:01:17 am »
Huh? Where am I doing that?

cdbc

  • Hero Member
  • *****
  • Posts: 1334
    • http://www.cdbc.dk
Re: Dynamically adding and freeing controls
« Reply #10 on: June 23, 2024, 07:26:25 am »
Hi
I think it's 'TComponent' that takes care of that.
You can try and follow an owned component when you free it, by ctrl+clickin' the different methods, that gets called...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

indydev

  • Full Member
  • ***
  • Posts: 109
Re: Dynamically adding and freeing controls
« Reply #11 on: June 23, 2024, 03:27:34 pm »
...weird.  After stepping through several more times. I no longer have the issue. I can call controls[n].free without a problem. ..without RemoveControl.  I must have done something, but I was changing things around and following through the code so many times I can't see anything that is really different from when I first made the controls[n].free;

note: I type controls[n] because controls['i'] triggers the italic switch and removes the array index.

« Last Edit: June 23, 2024, 03:34:45 pm by indydev »

jamie

  • Hero Member
  • *****
  • Posts: 6370
Re: Dynamically adding and freeing controls
« Reply #12 on: June 23, 2024, 03:36:08 pm »
...weird.  After stepping through several more times. I no longer have the issue. I can call controls[n].free without a problem. ..without RemoveControl

Pixie dust, must be magic!  :o
The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018