Sven
Sven

Reputation: 63

Custom GridPanel ControlItems Issue

I am subclassing TGridPanel to my control TMyGridPanel.

I do this because i want to add 4 default buttons in the GridPanel.

So i override the constructor and create the buttons like:

constructor TMyGridPanel.Create(AOwner: TComponent);
var
  i: Integer;
  btn: TButton;
begin
  inherited Create(AOwner);

  for i := 0 to 3 do
  begin
    btn := TButton.Create(Self);
    btn.Parent := Self;
    btn.Align := alClient;
    btn.Caption := 'Hello World';    
    btn.Visible := True;
  end;
end;

This is working fine. The ControlCollection Items property shows 4 Buttons as CollectionItems .

Now i want to copy and paste (duplicate) my control because i want to have 2 of it. However when i do it the buttons don't show up in the control.

The ControlCollection Items property shows 4 Collection Items but they don't have a name (empty). When i close the form and reopen it the buttons appear.

I am trying to fix this problem for some days now but can't figure it out.

Upvotes: 4

Views: 517

Answers (1)

quasoft
quasoft

Reputation: 5448

Problem:

When you copy your panel component to clipboard, all its published properties are streamed into text (paste it in notepad to see how it looks).

Pasting to the form reconstructs the component back from this text.

And as ControlCollection property is defined in Vcl.ExtCtrls.TGridPanel as published, buttons within it are included in this text. Here is an excerpt:

object MyGridPanel1: TMyGridPanel
  Left = 64
  ...
  ControlCollection = <
    item
      Column = 0
      Control = Button9
      Row = 0
    end
    item
      Column = 1
      Control = Button10
      Row = 0
    end
    ...
  object Button9: TButton
    Left = 1
    ...
  end
  object Button10: TButton
    Left = 92
    ...
  end
  ...
end

When pasting, the IDE designer first creates a new object of class TMyGridPanel. During this step the constructor of TMyGridPanel creates a new set of buttons.

After that all published properties get reconstructed from the text, including the ControlCollection and Buttons within it, and this is where problem lies.

Possible solution:

A possible solution in this situation is to change parent class of TMyGridPanel to TCustomGridPanel

TMyGridPanel2 = class(TCustomGridPanel)
...

TCustomGridPanel (similar to other TCustom... components) does not publish any of its properties, so they won't get streamed into clipboard.

Actually inheriting from TCustom... variants of controls, and not from the one registered in Component Pallet, is the right way to subclass components.

If we now copy this variant of TMyGridPanel2 to clipboard and paste it in notepad, we can see that there no additional properties:

object MyGridPanel21: TMyGridPanel2
  Left = 184
  Top = 200
  Width = 185
  Height = 41
end

Drawbacks:

This approach works, but have several cons that has to be noted:

  • You cannot access custom properties introduced by TGridPanel in Object Inspector (but you can access them at runtime). A workaround to bring a property back in Object Inspector, is to add it in published section of your component:

    TMyGridPanel2 = class(TCustomGridPanel)
    public
        ...
    published
        property BorderStyle;
        property ColumnCollection;
        property RowCollection;
        ...
    end;
    
  • You cannot change properties of the four buttons via Object Inspector, nor attach events to them. You have to do that in code.

    Actually this is good behavior. When you create a composite component that has child controls, it is good practice to have all functionality contained within the component itself.

Full code sample:

unit MyGridPanel2;

interface

uses
  Classes, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Controls;

type
  TMyGridPanel2 = class(TCustomGridPanel)
  private
  public
    constructor Create(AOwner: TComponent); override;
  published
  end;

procedure Register;

implementation

{ TMyGridPanel2 }

constructor TMyGridPanel2.Create(AOwner: TComponent);
var
  i: Integer;
  btn: TButton;
begin
  inherited Create(AOwner);

  for i := 0 to 3 do
  begin
    btn := TButton.Create(Self);
    btn.Parent := Self;
    btn.Align := alClient;
    btn.Caption := 'Hello World';
    btn.Visible := True;
  end;
end;

procedure Register;
begin
  RegisterComponents('Custom', [TMyGridPanel2]);
end;

end.

Try this in test project first, not in production.

Upvotes: 4

Related Questions