3

When we create a component as a custom control and the control is dropped on a panel the control always appears on the form rather than the containing control. How do you set the parent of the custom control in Create so that when the button is dropped on panel the buttons parent is the panel?

TGlassButton = class(TCustomControl)
...
public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
...

constructor TGlassButton.Create(AOwner: TComponent);
  begin
    inherited;  ???????????
    inherited Create(AOwner); ????????????
    Parent := TWinControl( AComponent ); ??????????????
    ...
  end;

The problem is designtime creation not runtime. This works perfectly:

procedure TForm10.FormCreate(Sender: TObject);
begin
  GlassButton0 := TGlassButton.Create( Panel1 );
  GlassButton0.Parent := Panel1;
  GlassButton0.Left := 20;
  GlassButton0.Top := 6;
  GlassButton0.Width := 150;
  GlassButton0.Height := 25;
  GlassButton0.Caption := 'Created At RunTime';
end;
Bill
  • 2,993
  • 5
  • 37
  • 71
  • 3
    The behaviour you are looking for happens for free. Something else in your code must be preventing it. The solution is most certainly not to be setting `Parent` in your constructor! – David Heffernan Jun 19 '11 at 15:21
  • You don't have to do anything special, normally a TCustomControl descendant works like that out of the box. You have probably written some code which interferes with normal VCL control creation. Show your code. – Ondrej Kelle Jun 19 '11 at 15:23
  • Is it possible that you aren't selecting the panel before you drop the control onto the form? – David Heffernan Jun 19 '11 at 15:36
  • It does not matter if the panel is selected before dropping the control. The form is always the parent at designtime. – Bill Jun 19 '11 at 15:47
  • It could be a misbehaving custom component on the same form, or a wrongly implemented designtime package. Again, normally a TCustomControl descendant is dropped on a panel as expected. What other code does your TGlassButton contain? Are you using other custom components doing something suspicious on the same form at design time? – Ondrej Kelle Jun 19 '11 at 22:20
  • https://stackoverflow.com/questions/56859524/where-to-initialize-subcomponent-parent – Gabriel Nov 24 '20 at 17:36

2 Answers2

6

DO NOT set the Parent property in the constructor! As others have said, the IDE and DFM streaming systems will assign the Parent automatically AFTER the constructor has exited. If you need to perform operations in your constructor that are dependant on a Parent being assigned, then you need to re-design your component. Override the virtual SetParent() and/or Loaded() methods and do your operations from there instead. And make use of if (csDesigning in ComponentState) then ... checks in places where you can avoid operations that are not actually needed at design-time.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Moving handle specific code from the contructor to `Loaded` did the trick for me :D – Jerry Dodge Feb 20 '15 at 17:58
  • @JerryDodge - Loaded is not a good place to do this as it is only called when the control is loaded from DFM, not also when it is created dynamically. – Gabriel Feb 27 '17 at 11:31
  • @NAZCA Creating a form dynamically still loads from the DFM. The only time it doesn't load the DFM is when there isn't any, which is very rare. – Jerry Dodge Feb 27 '17 at 14:15
  • @JerryDodge-I am talking about a control that holds other controls, not a form. – Gabriel Feb 27 '17 at 14:58
  • not just that. Dynamically allocating any control, ie calling `TGlassButton.Create()` directly, will not call its `Loaded()` method. Only DFM streaming does that. Hence my suggestion to override `SetParent()` as an alternative. – Remy Lebeau Feb 27 '17 at 15:23
  • Why not in CreateParams or CreateWnd? – Gabriel Jul 01 '19 at 09:37
  • @Rigel because they can get called multiple times during a control's lifetime. They are not appropriate places to manage control creation – Remy Lebeau Jul 01 '19 at 16:15
4

Parents should be set by whomever is creating the control. For controls created at design time, this would be done by the streaming system when the form is created. For controls created at run-time, it should be done when the control is created:

var
  Control: TWinControl;
begin
  Control := TGlassButton.Create(<Form or Application>);
  Control.Parent := <Some other control on the form>;
end;

Please note that in general the form is the owner of all controls on it, regardless of parent-ing. The Parent of a control is / should be the control responsible for painting it: in other words the control in which it is visually located. Ie a Panel, TabSheet, GroupBox or some other container.

Gabriel
  • 20,797
  • 27
  • 159
  • 293
Marjan Venema
  • 19,136
  • 6
  • 65
  • 79
  • @Altar: Remy is quite right. Controls created at run-time does not imply that they must be created in the constructor. I often use an AfterConstruction override to ensure everything in the construction process, including streaming and calling Loaded for controls created at design-time, has finished. – Marjan Venema Sep 12 '12 at 15:18
  • 1
    @Rigel "*... contradicts what Remy Lebeau says*" - no it doesn't. I said do not set the `Parent` from inside the constructor of the child control that is being created. In Marjan's example, the `Parent` is being set after the child's constructor has exited, which is perfectly fine – Remy Lebeau Jul 01 '19 at 16:19