自定义 GridPanel 控件项问题

4

我正在将 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。现在我想复制和粘贴(重复)我的控件,因为我想要两个。但是当我这样做时,按钮不会出现在控件中。 ControlCollection项属性显示4个集合项,但它们没有名称(空白)。当我关闭表单并重新打开它时,按钮就会出现。 我已经尝试解决这个问题好几天了,但是无法解决。

你说“名称”。原来的四个按钮有名称吗?动态创建的控件通常具有空名称属性,直到您分配一个名称。难道你的意思是“标题”吗? - Blurry Sterk
如果您已经覆盖了构造函数,那么我怀疑您没有重写OnCreate。两者都不需要。 - Blurry Sterk
@BlurrySterk 名称属性始终为空。但是,创建控件(而不是复制它)时,集合项将显示为“MyGrid。”。当我复制控件时,它什么也不显示“”。我编辑了帖子并将OnCreate更正为Constructor。 - Sven
1个回答

4

问题:

当您将面板组件复制到剪贴板时,所有已发布的属性都被转换成文本(粘贴到记事本中查看其外观)。

将其粘贴到表单上会从此文本中重新构建组件。

由于Vcl.ExtCtrls.TGridPanel中的ControlCollection属性被定义为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设计器首先创建一个TMyGridPanel类的新对象。在此步骤中,TMyGridPanel的构造函数会创建一组新的按钮。
之后,所有已发布的属性都从文本中重建,包括其中的ControlCollection和Buttons,这就是问题所在。 可能的解决方案: 在这种情况下,一个可能的解决方案是将TMyGridPanel的父类更改为TCustomGridPanel
TMyGridPanel2 = class(TCustomGridPanel)
...

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

实际上,从控件的TCustom...变体继承,而不是从在组件面板中注册的变体继承,是子类化组件的正确方式。

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

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

缺点:

这种方法可行,但有几个需要注意的缺点:

  • 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.

完整代码示例:

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.

首先在测试项目中尝试,而不是在生产环境中进行。


1
谢谢你的回答! - Sven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接