Q:

Getting column index of a clicked control in TGridPanel

I'm using a TGridPanel to hold some panels.  At design time, I've set the grid panel to have 1 row and 5 columns.
I can add a panel to the grid using this code, which works well:
procedure TForm6.AddPanelToGrid(const ACaption: string);
var
  pnl: TPanel;
begin
  pnl := TPanel.Create(gpOne);
  pnl.Caption := ACaption;
  pnl.Parent := gpOne;
  pnl.Name := 'pnlName' + ACaption;
  pnl.OnClick := gpOne.OnClick;
  pnl.ParentBackground := false;
  pnl.ParentColor := false;
  pnl.Color := clLime;
  pnl.Font.Size := 14;
  gpOne.ControlCollection.AddControl(pnl);
  pnl.Height := pnl.Width;
end;

What I want to do is remove a TPanel from the grid when I click on it (which is why I have set the on click handler to that of the grid panel in the above code).
In that click handler I do this, which almost works:
procedure TForm6.gpOneClick(Sender: TObject);
begin
  if not (sender is TPanel) then exit;

  gpOne.ControlCollection.RemoveControl(Sender as TPanel);
  (Sender as TPanel).Free;

  gpOne.UpdateControlsColumn( 0 );  <<<-------
  gpOne.UpdateControlsRow(0);

  gpOne.Refresh();
end;

Using a parameter for UpdateControlColumn() causes the order of the panels in the grid to change - the first and second swap places.
I can get around this by adding the column idex to the panel's tag property, then pass that to UpdateControlColumn().  This then works, but once a panel has been removed the higher tag numbers are no longer valid - the panels have moved column.
So, how can I get the column that a panel is in from within the OnClick handler?  
I'm using Delphi 10.1 Berlin - if that makes any difference.
To test this, I started a new project, added a TGridPanel, set it to have 1 row and 5 equally widthed columns. I added 6 TButton controls and created an OnClick handler for each with the following code:
AddPanelToGrid('One');  // changing the string for each button.

Click a few buttons to add some panels, then click the panels to remove them.

A:

TCustomGridPanel has a pair of useful functions, CellIndexToCell() and CellToCellIndex, but they are not public and thus not directly accessible from a TGridPanel.
To make them available declare TGridPanel anew as below:
type
  TGridPanel = class(Vcl.ExtCtrls.TGridPanel)  // add this
  end;                                         // -"-
  TForm27 = class(TForm)
    Button1: TButton;
    gpOne: TGridPanel;
    ...
  end;

Then add rand c variables for row and col, add the call to CellIndexToCell() and use c as argument for UpdateControlsColumn:
procedure TForm27.gpOneClick(Sender: TObject);
var
  r, c: integer;
begin
  if not (sender is TPanel) then exit;

  gpOne.CellIndexToCell(gpOne.ControlCollection.IndexOf(Sender as TPanel), c, r); // add this

  gpOne.ControlCollection.RemoveControl(Sender as TPanel);
  (Sender as TPanel).Free;

  gpOne.UpdateControlsColumn( c );  // <<<-------
  gpOne.UpdateControlsRow(0);

  gpOne.Refresh();
end;

And follow advise of Remy Lebeau, regarding freeing the panel. ( I just noticed his comment).

If you haven't already, you may also want to take a look at TFlowPanel and its FlowStyle property. TflowPanel reordering after deletion is more predictable if you use more than one row, but depends of course on what you need.

