自定义 GridPanel ControlItems 问题

Custom GridPanel ControlItems Issue

我正在将 TGridPanel 子类化到我的控件 TMyGridPanel

我这样做是因为我想在 GridPanel.

中添加 4 个默认按钮

所以我重写 constructor 并创建如下按钮:

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;

这工作正常。 ControlCollection 项 属性 显示 4 个按钮 CollectionItems .

现在我想复制并粘贴(复制)我的控件,因为我想要其中的 2 个。 但是,当我这样做时,按钮不会出现在控件中。

ControlCollection 项 属性 显示 4 Collection 项,但它们没有名称(空)。 当我关闭表单并重新打开它时,会出现按钮。

我已经尝试解决这个问题好几天了,但还是搞不明白。

问题:

当您将面板组件复制到剪贴板时,其所有已发布的属性都会流式传输到文本中(将其粘贴到记事本中以查看其外观)。

粘贴到表单会从该文本重建组件。

并且由于 ControlCollection 属性 在 Vcl.ExtCtrls.TGridPanel 中定义为 published,其中的按钮包含在本文中。以下是摘录:

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

粘贴时,IDE设计者先新建一个class对象TMyGridPanel。在此步骤中,TMyGridPanel 的构造函数创建了一组新按钮。

之后所有已发布的属性都从文本中重建,包括其中的 ControlCollection 和 Buttons,这就是问题所在。

可能的解决方案:

在这种情况下可能的解决方案是将 TMyGridPanel 的父级 class 更改为 TCustomGridPanel

TMyGridPanel2 = class(TCustomGridPanel)
...

TCustomGridPanel(类似于其他 TCustom... 组件)不会发布其任何属性,因此它们不会流式传输到剪贴板。

实际上继承 TCustom... 控件的变体,而不是从 Component Pallet 中注册的控件继承,才是子class 组件的正确方法。

如果我们现在将 TMyGridPanel2 的这个变体复制到剪贴板并将其粘贴到记事本中,我们可以看到没有其他属性:

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

缺点:

这种方法有效,但有几个必须注意的缺点:

  • 您无法在对象检查器中访问 TGridPanel 引入的自定义属性(但您可以在运行时访问它们)。 将 属性 带回对象检查器的解决方法是将其添加到组件的 published 部分:

    TMyGridPanel2 = class(TCustomGridPanel)
    public
        ...
    published
        property BorderStyle;
        property ColumnCollection;
        property RowCollection;
        ...
    end;
    
  • 您不能通过对象检查器更改四个按钮的属性,也不能为它们附加事件。您必须在代码中执行此操作。

    其实这是很好的行为。当您创建具有子控件的复合组件时,最好将所有功能都包含在组件本身中。

完整代码示例:

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.

首先在测试项目中尝试这个,而不是在生产中。