5

I get this error when I drop my control on a form. The error appears here:

  TAssociateFileExt = class(TGroupBox)
  private
  protected
  public
    btnAssociate   : TButton;
    constructor Create(aOwner: TComponent); override;
  end;

constructor TAssociateFileExt.Create(aOwner: TComponent);
begin
 inherited Create(aOwner);
 Caption:= '';                       <-------- error here
 ClientHeight:= 125;                 <-------- and here
 ClientWidth := 170;
 DoubleBuffered:= TRUE;

 btnAssociate:= TButton.Create(Self);
 btnAssociate.Parent:= Self;
 btnAssociate.Visible:= TRUE;
 btnAssociate.Left:= 17;
 btnAssociate.Top:= 26;
 btnAssociate.Width:= 142;
 btnAssociate.Height:= 25;
 btnAssociate.Hint:= 'Associate this application with its files. When you double click a file this program will automatically start and load that file.';
 btnAssociate.Caption:= 'Associate';
 btnAssociate.DoubleBuffered:= TRUE;
 btnAssociate.ParentDoubleBuffered:= FALSE;
 btnAssociate.TabOrder:= 0;
 btnAssociate.OnClick:= btnAssociateClick;

 btnAssociateDel:= TButton.Create(Self);
 btnAssociateDel.Parent:= Self;
 btnAssociateDel.Visible:= TRUE;
 btnAssociateDel.Left:= 17;
 btnAssociateDel.Top:= 53;
 btnAssociateDel.Width:= 142;
 btnAssociateDel.Height:= 25;
 btnAssociateDel.Hint:= 'Remove association';
 btnAssociateDel.Caption:= 'Remove association';
 btnAssociateDel.DoubleBuffered:= TRUE;
 btnAssociateDel.ParentDoubleBuffered:= FALSE;
 btnAssociateDel.TabOrder:= 1;
 btnAssociateDel.OnClick:= btnAssociateDelClick;

 chkAllUsers:= TCheckBox.Create(Self);
 chkAllUsers.Parent:= Self;
 chkAllUsers.Visible:= TRUE;
 chkAllUsers.Left:= 31;
 chkAllUsers.Top:= 97;
 chkAllUsers.Width:= 115;
 chkAllUsers.Height:= 17;
 chkAllUsers.Hint:= 'Please note that if you want to do this for all users then you need administrator/elevated rights.';
 chkAllUsers.Caption:= 'Do this for all users';
 chkAllUsers.DoubleBuffered:= TRUE;
 chkAllUsers.ParentDoubleBuffered:= FALSE;
 chkAllUsers.TabOrder:= 2;
 chkAllUsers.OnClick:= chkAllUsersClick;
end;

Probably the answer is 'Caption needs a valid window handle'. Right? However, an article by David Intersimone (here) says it is ok to set Caption in the constructor.

  1. Is the article wrong?
  2. Should I move the code (Caption and TButton.Create) in CreateWnd (since AfterConstruction is not a good place)? The thing is that CreateWnd can be called more than once: "CreateWnd is called automatically when the control is first created or when the underlying screen object must be destroyed and recreated to reflect property changes."

Update:
After adding (aOwner: TComponent), as J... suggested, to constructor's declaration (in Implementation) the error moved to the next line (clientheight:= 90);

Server Overflow
  • 19,827
  • 22
  • 149
  • 277

2 Answers2

3

I moved the code in CreateWindowHandle. Now it works. Full code:

UNIT cAssociateExt;

INTERFACE

USES
  Windows, Messages, SysUtils, Classes, Controls, ExtCtrls, Forms, StdCtrls;

TYPE
  TAssociateFileExt = class(TGroupBox)
  private
  protected
  public
    btnAssociate   : TButton;
    btnAssociateDel: TButton;
    chkAllUsers    : TCheckBox;
    constructor Create(aOwner: TComponent); override;
    procedure AfterConstruction;  override;
    procedure CreateWindowHandle(const Params: TCreateParams); override;
    ...
  published
  end;


procedure Register;


IMPLEMENTATION


procedure TAssociateFileExt.AfterConstruction;
begin
 inherited;         //Not a good place here
end;


procedure TAssociateFileExt.CreateWindowHandle(const Params: TCreateParams);
begin
 inherited;

 //DO NOT CREATE CONTROLS HERE! See: Sertac Akyuz's comment

 Caption:= '';    
 ClientHeight:= 125;
 ClientWidth := 170;
end;



constructor TAssociateFileExt.Create(aOwner: TComponent);
begin
 inherited Create(aOwner);
 DoubleBuffered:= TRUE;

 btnAssociate:= TButton.Create(Self);
 btnAssociate.Parent:= Self;
 btnAssociate.Visible:= TRUE;
 btnAssociate.Left:= 17;
 ...
end;
Server Overflow
  • 19,827
  • 22
  • 149
  • 277
  • 1
    If you're creating instances in CreateWindowHandle, consider destroying them in its counterpart: DestroyWindowHandle. Better use CreateWnd/WindowHandle for only things that require a window handle. – Sertac Akyuz Apr 28 '14 at 18:31
  • @SertacAkyuz - I don't understand. I have to manually free btnAssociate (and the other controls)? btnAssociate.Owner is set to Self so Self should free btnAssociate. Right? – Server Overflow Apr 28 '14 at 18:40
  • You don't have to. But if your groupbox's window is recreated for whatever reason, you'll have alive buttons which you don't have any reference to, they'll be destroyed only when the groupbox is destroyed. – Sertac Akyuz Apr 28 '14 at 18:55
  • 2
    Create the buttons in OnCreate as before. Only use 'CreateWindowHandle' to set properties that require a window handle, like setting client width/height. – Sertac Akyuz Apr 28 '14 at 19:08
  • Well, my comments were just a few notes. What matters is the requirement for the handle for *some* of the operations. Please integrate yourself anything you like to the answer from the comments. – Sertac Akyuz May 06 '14 at 11:05
  • NOTE: 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. – Server Overflow Feb 27 '17 at 11:31
  • A nice source of information: https://stackoverflow.com/questions/582903 – Server Overflow Nov 19 '18 at 08:13
-2

IMPORTANT: When you are creating components you need to use this lines in the constructor procedure for avoid the "Control has no parent window".

inherited Create(AOwner); parent:=TWinControl(AOwner);