delphivcldelphi-10.1-berlintgridpanel

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.


Solution

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