Assuming you might want to be able to scroll within your StringGrid and have the Buttons beeing associated with the selected row, you will have to implement an handler for TopLeftChanged. The buttons won't be moved if you scroll in your Stringgrid, without implementing code for this.
procedure TForm3.SpeedButton1Click(Sender: TObject);
begin
Showmessage(TSpeedButton(Sender).Name + ' ' + IntToStr(TSpeedButton(Sender).Tag));
end;
const
C_COL = 4;
procedure TForm3.StringGrid1TopLeftChanged(Sender: TObject);
var
point: TPoint;
btn: TSpeedButton;
row: integer;
rect: TRect;
y: integer;
begin
rect := TStringGrid(Sender).CellRect(C_COL, TStringGrid(Sender).TopRow);
point := ScreenToClient(ClientToScreen(rect.TopLeft));
y := rect.Top;
for row := 0 to TStringGrid(Sender).RowCount - 1 do
begin
btn := TSpeedButton(TStringGrid(Sender).FindComponent(Format('SP%d', [row])));
if row >= TStringGrid(Sender).TopRow then
begin
btn.Top := y;
btn.Left := rect.Left;
btn.Visible := rect.Right > 0;
y := y + TStringGrid(Sender).DefaultRowHeight;
end
else
btn.Visible := false;
end;
end;
procedure TForm3.FormCreate(Sender: TObject);
var
point: TPoint;
btn: TSpeedButton;
row: integer;
rect: TRect;
y: integer;
begin
rect := StringGrid1.CellRect(C_COL, StringGrid1.TopRow);
point := ScreenToClient(ClientToScreen(rect.TopLeft));
y := rect.Top;
for row := 0 to StringGrid1.RowCount - 1 do
begin
btn := TSpeedButton.Create(StringGrid1);
btn.Name := Format('SP%d', [row]);
btn.Parent := StringGrid1;
btn.OnClick := SpeedButton1Click;
btn.tag := row;
btn.Width := StringGrid1.ColWidths[C_COL];
btn.Height := StringGrid1.DefaultRowHeight;
btn.Visible := false;
end;
StringGrid1TopLeftChanged(TStringGrid(Sender));
end;
an enhanced version as suggested by @Tlama would make it necessary to implement an interposer class or use an own component to override ColWidthsChanged and RowHeightsChanged to keep the buttons painted correct not just on scrolling but on row/column sizing.
//.....
type
TStringGrid=Class(Grids.TStringGrid)
procedure ColWidthsChanged; override;
procedure RowHeightsChanged; override;
End;
TForm3 = class(TForm)
StringGrid1: TStringGrid;
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
procedure StringGrid1TopLeftChanged(Sender: TObject);
private
procedure SpeedButton1Click(Sender: TObject);
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
{ TStringGrid }
procedure TStringGrid.ColWidthsChanged;
begin
inherited;
TopLeftChanged;
end;
procedure TStringGrid.RowHeightsChanged;
begin
inherited;
TopLeftChanged;
end;
procedure TForm3.SpeedButton1Click(Sender: TObject);
begin
Showmessage(TSpeedButton(Sender).Name + ' ' + IntToStr(TSpeedButton(Sender).Tag));
end;
const
C_COL = 4;
procedure TForm3.StringGrid1TopLeftChanged(Sender: TObject);
var
point: TPoint;
btn: TSpeedButton;
row: integer;
rect: TRect;
y: integer;
begin
for row := 0 to TStringGrid(Sender).RowCount - 1 do
begin
btn := TSpeedButton(TStringGrid(Sender).FindComponent(Format('SP%d', [row])));
if row >= TStringGrid(Sender).TopRow then
begin
rect := TStringGrid(Sender).CellRect(C_COL, row);
btn.BoundsRect := rect;
btn.Visible := rect.Right > 0;
y := y + TStringGrid(Sender).DefaultRowHeight;
end
else
btn.Visible := false;
end;
end;
procedure TForm3.FormCreate(Sender: TObject);
var
point: TPoint;
btn: TSpeedButton;
row: integer;
rect: TRect;
y: integer;
begin
rect := StringGrid1.CellRect(C_COL, StringGrid1.TopRow);
point := ScreenToClient(ClientToScreen(rect.TopLeft));
y := rect.Top;
for row := 0 to StringGrid1.RowCount - 1 do
begin
btn := TSpeedButton.Create(StringGrid1);
btn.Name := Format('SP%d', [row]);
btn.Parent := StringGrid1;
btn.OnClick := SpeedButton1Click;
btn.tag := row;
btn.Visible := false;
end;
StringGrid1TopLeftChanged(TStringGrid(Sender));
end;