Search code examples
delphiduplicatescomponentsvcloncreate

Delphi TWinControl duplicating its children at runtime


When I build this component and place it on a form, all appears fine. However, when I run the program, the control duplicates all of its children. It appears to be calling the initialize components twice, although I cannot see why.

Code from TWinControl Component

    unit Payments_Test;

    interface

    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
      Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvSmoothButton, EBSGrid,
       Vcl.StdCtrls, Vcl.ExtCtrls, Data.DB, MemDS, EBS3DataClass,
      DBAccess, AdvUtil, Vcl.Grids, AdvObj, BaseGrid, AdvGrid,AdvStyleIF;

    type
      TEBSPayments_Test = class(TWinControl)
      private
        { Private declarations }
        fDeletePayment, fNewPayment : TNotifyEvent;
        PYLabel33: TLabel;
        lblOverPayment: TLabel;
        cmdAddPayment: TAdvSmoothButton;
        cmdDeletePayment: TAdvSmoothButton;
        PYPanel1: TPanel;
        PyGrid1: TEBSGrid;
        FBackgroundColor, FPanelColor: TColor;
        procedure cmdDeletePaymentClick(Sender: TObject);
        procedure cmdAddPaymentClick(Sender: TObject);
        procedure SetBackgroundColor(const Value: TColor);
        procedure SetPanelColor(const Value: TColor);
        procedure InitializeComponents;
      Protected
        procedure SetParent(AParent: TWinControl); override;
      Published
        property Anchors  default [akLeft, akTop];
        property Align  default alNone;
        property AutoSize Default True;
        property PanelColor: TColor read FPanelColor write SetPanelColor default clSkyBlue;
        property BackgroundColor: TColor read FBackgroundColor write 
        SetBackgroundColor default clSkyBlue;
        property DeletePayment: TNotifyEvent read FDeletePayment write FDeletePayment;
        property NewPayment: TNotifyEvent read FNewPayment write FNewPayment;
      public
        { Public declarations }
        procedure Initialise;
        procedure CloseControl;
        constructor Create(AOwner: TComponent); override;
        Destructor Destroy; Override;
      end;

    Procedure Register;

    implementation


    Uses EBS3DataUtils;

    procedure TEBSPayments_Test.SetBackgroundColor(const Value: TColor);
    begin
      if FBackgroundColor <> Value then
      begin
        FBackgroundColor := Value;
        Color := FBackgroundColor;
      end;
    end;

    procedure TEBSPayments_Test.SetPanelColor(const Value: TColor);
    begin
      if FPanelColor <> Value then
      begin
        FPanelColor := Value;
        Color := FPanelColor;
      end;
    end;


    constructor TEBSPayments_Test.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      Width := 428;
      Height := 224;
    end;

    procedure TEBSPayments_Test.InitializeComponents;
    begin
      PYPanel1 := TPanel.Create(Self);
      PYPanel1.Parent := Self;
      PYPanel1.Height := 22;
      PYPanel1.Align := alTop;
      PYPanel1.Caption := '';
      PyGrid1 := TEBSGrid.Create(Self);
      PYGrid1.Parent := Self;
      PYGrid1.AutoSize := False;
      PYGrid1.Align := alBottom;
      PYGrid1.Height := ClientHeight - PYPanel1.Height;
      PYGrid1.FctlGrid.FixedCols := 0;
      FBackgroundColor := clSkyBlue;
      FPanelColor := clMoneyGreen;
      PYPanel1.Color := FPanelColor;
      Color := FBackgroundColor;
      cmdDeletePayment := TAdvSmoothButton.Create(Self);
      cmdDeletePayment.Parent := PYPanel1;
      cmdDeletePayment.SetBounds(182,1,54,20);
      cmdDeletePayment.UIStyle := tsCustom;
      cmdDeletePayment.Caption := 'Delete';
      cmdDeletePayment.Color := clBlue;
      cmdDeletePayment.Appearance.Font.Color := clWhite;
      cmdDeletePayment.Appearance.Rounding := 8;
      cmdDeletePayment.Bevel := True;
      cmdDeletePayment.BevelColor := clWhite;
      cmdAddPayment := TAdvSmoothButton.Create(Self);
      cmdAddPayment.Parent := PYPanel1;
      cmdAddPayment.SetBounds( 127,1,49,20);
      cmdAddPayment.UIStyle := tsCustom;
      cmdAddPayment.Caption := 'New';
      cmdAddPayment.Color := clBlue;
      cmdAddPayment.Appearance.Font.Color := clWhite;
      cmdAddPayment.Appearance.Rounding := 8;
      cmdAddPayment.Bevel := True;
      cmdAddPayment.BevelColor := clWhite;
      lblOverPayment := TLabel.Create(Self);
      lblOverPayment.Parent := PYPanel1;
      lblOverPayment.SetBounds(483,5,116,13);
      lblOverPayment.Font.Color := clRed;
      lblOverPayment.Font.Style := [fsBold];
      PYLabel33 := TLabel.Create(Self);
      PYLabel33.Parent := PYPanel1;
      PYLabel33.SetBounds(8,6,66,13);
      PYLabel33.Font.Color := clGreen;
      PYLabel33.Font.Style := [fsBold];
    end;

    procedure TEBSPayments_Test.SetParent(AParent: TWinControl);
    begin
      inherited SetParent(AParent);
      if Assigned(AParent) then  InitializeComponents;
    end;


    Destructor TEBSPayments_Test.Destroy;
    begin
      PYLabel33.Free;
      lblOverPayment.Free;
      cmdAddPayment.Free;
      cmdDeletePayment.Free;
      PYPanel1.Free;
      PyGrid1.Free;
      Inherited Destroy;
    end;


    procedure TEBSPayments_Test.Initialise;
    begin
    end;

    procedure TEBSPayments_Test.CloseControl;
    begin
      PYGrid1.SaveGridSettings;
    end;

    procedure TEBSPayments_Test.cmdAddPaymentClick(Sender: TObject);
    begin
      if Assigned(FNewPayment) then
        FNewPayment(Self);
    end;

    procedure TEBSPayments_Test.cmdDeletePaymentClick(Sender: TObject);
    begin
      if Assigned(FDeletePayment) then
        FDeletePayment(Self);
    end;


    procedure Register;
    begin
      RegisterComponents('EBSH', [TEBSPayments_Test]);
    end;


    end.

Code from the TForm

    unit ComponentTest;

    interface

    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, 
      System.Variants, System.Classes,Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs,AdvUtil, Vcl.Grids, AdvObj,
      BaseGrid, AdvGrid, UniProvider, SQLServerUniProvider,
       Vcl.StdCtrls,  JvExStdCtrls, JvMemo, Vcl.ExtCtrls,
      Notes, ContactPanel, Payments_Test;

    type
      TfrmCompTest = class(TForm)
        Button1: TButton;
        EBSPayments_Test1: TEBSPayments_Test;
      private
        { Private declarations }
      public
        { Public declarations }
      end;

    var
      frmCompTest: TfrmCompTest;

    implementation

    {$R *.dfm}

    end.

I checked the code for multiple calls to initialize components. At one stage it was doing it just by pasting to the form. This was caused by two calls to initialize components Now it only does it at run time. A breakpoint triggers twice but I cant figure where that second call comes from


Solution

  • As your container component is handling the lifetime of its subcomponents, you need to mark those as subcomponents so the streaming system can handle them properly. To do that call SetSubComponent(True) on each of the subcomponents right after its creation.

    For example:

      PYPanel1 := TPanel.Create(Self);
      PYPanel1.SetSubComponent(True);
      PYPanel1.Parent := Self;
      PYPanel1.Height := 22;
      PYPanel1.Align := alTop;
      PYPanel1.Caption := '';