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
相关产品推荐
相关产品推荐

