You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TDBGrid派生类应在何时创建子控件以保证Parent已赋值?

解决方案

你需要重写VCL控件的SetParent虚方法,该方法会在控件的Parent属性被赋值完成后由VCL框架自动触发,刚好匹配你需要的执行时机。

注意事项:

  • 构造函数中仅初始化ControlButton的基础属性,不处理Parent、位置相关的赋值
  • 在SetParent中为ControlButton绑定父容器,同时同步位置
  • 额外重写SetBounds方法,保证网格位置/大小变化时,控制按钮可以同步跟随移动

修改后的完整代码如下:

type TAEDBGrid = class(TDBGrid)
  private
    ControlButton: TButton;
    procedure ControlButton_Click(Sender: TObject);
    procedure UpdateControlButtonPos; // 新增:同步按钮位置的公共方法
  protected
    procedure SetParent(AParent: TWinControl); override; // 重写SetParent
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override; // 重写SetBounds同步位置
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
end;

constructor TAEDBGrid.Create(AOwner: TComponent);
var Menu: TPopupMenu;
    Item: TMenuItem;
begin
  inherited Create(AOwner);

  // 仅初始化按钮基础属性,不处理Parent、位置
  ControlButton := TButton.Create(Self); // Owner设为Self,降低内存泄漏风险
  ControlButton.Caption := '...';
  ControlButton.Font.Style := Self.Font.Style + [fsBold];
  ControlButton.Height := 19;
  ControlButton.Width := 17;
  ControlButton.OnClick := ControlButton_Click;

  // 初始化菜单,保持原有逻辑
  Menu := TPopupMenu.Create(ControlButton);
  Item := TMenuItem.Create(Menu);
  Item.Caption := 'Select Columns';
  Item.OnClick := ControlButton_SelectColumns;
  Menu.Items.Add(Item);
  Item := TMenuItem.Create(Menu);
  Item.Caption := 'Export to Excel';
  Item.OnClick := ControlButton_ExportToExcel;
  Item.Visible := False;
  Menu.Items.Add(Item);
  ControlButton.PopupMenu := Menu;
end;

destructor TAEDBGrid.Destroy;
begin
  ControlButton.Free;
  inherited Destroy;
end;

procedure TAEDBGrid.SetParent(AParent: TWinControl);
begin
  inherited;
  // 父容器赋值完成后,同步按钮的父容器
  if Assigned(ControlButton) then
  begin
    ControlButton.Parent := AParent;
    UpdateControlButtonPos;
  end;
end;

procedure TAEDBGrid.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
  inherited;
  // 网格位置/大小变化时同步按钮位置
  UpdateControlButtonPos;
end;

procedure TAEDBGrid.UpdateControlButtonPos;
begin
  if Assigned(ControlButton) and Assigned(ControlButton.Parent) then
  begin
    ControlButton.Top := Self.Top + 1;
    ControlButton.Left := Self.Left + 1;
    // 如果需要按钮固定在网格右上角等其他位置,可在此调整计算逻辑
  end;
end;

procedure TAEDBGrid.ControlButton_Click(Sender: TObject);
var Position: TPoint;
begin
  if GetCursorPos(Position) then ControlButton.PopupMenu.Popup(Position.X, Position.Y);
end;

如果你的控件是设计时放在窗体上的,这套逻辑会在窗体加载完成、给网格赋值Parent的时候自动触发按钮的父容器绑定;如果是运行时动态创建的网格,只要你给网格赋值Parent属性,按钮就会自动完成挂载显示,不需要额外调用方法。

内容的提问来源于stack exchange,提问作者Marc Guillot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 04:57:04