Quick Tip: Dynamically Add Rows, Columns and Controls to Delphi’s TGridPanel


Delphi’s TGridPanel control is an ideal pick when you want to create grid-flow like user interface layout for your controls.

To place controls on a grid panel you specify the number of rows and columns (RowCollection and ColumnCollection properties) and simply drop a control on it. Unlike standard TPanel, when you drop a control on a GridPanel it will be placed in the next available empty cell in the grid. As you add more controls the grid will grow either by rows or columns being added automagically (or not, if the ExpandStyle poroperty is set to emFixedSize). What’s more you can even determine how each cell will be sized: will it have a fixed size, or a percentage of the grid size – so you can have controls nicely uniformly distributed in a grid.

That’s all great when you know the design at design-time – and you know what number of controls you want to be hosted by the grid panel.

What if you need to dynamically add controls to grid panel at run-time?

Say you do not know the number of rows or columns at design time, but you need to add controls to the grid panel dynamically where the number of rows/columns (/controls) changes. You’ll have to deal with RowCollection and ColumnCollection properties (to add or remove rows/columns). You’ll have to specify the SizeStyle for each cell and finally you have to somehow add a control to each cell.

So, without further ado, here’s how to add controls to GridPanel dynamically:

procedure TForm1.FormCreate(Sender: TObject);
begin
  GridPanel1.Caption := '';

  CreateButtonGrid(3,5);
end;

procedure TForm1.CreateButtonGrid(const rowCount, colCount : integer);
var
  i : integer;
  aButton: TButton;
begin
  GridPanel1.RowCollection.BeginUpdate;
  GridPanel1.ColumnCollection.BeginUpdate;

  for i := 0 to -1 + GridPanel1.ControlCount do
    GridPanel1.Controls[0].Free;

  //btw, cannot clear if there are controls, so first remove "old" controls above
  GridPanel1.RowCollection.Clear; 
  GridPanel1.ColumnCollection.Clear;

  for i := 1 to rowCount do
    with GridPanel1.RowCollection.Add do
    begin
      SizeStyle := ssPercent;
      Value := 100 / rowCount; //have cells evenly distributed
    end;

  for i := 1 to colCount do
    with GridPanel1.ColumnCollection.Add do
    begin
      SizeStyle := ssPercent;
      Value := 100 / colCount; //have cells evenly distributed
    end;

  for i := 0 to -1 + rowCount * colCount do
  begin
    aButton := TButton.Create(self);
    aButton.Parent := GridPanel1; //magic: place in the next empty cell
    aButton.Visible := true;
    aButton.Caption := 'Btn ' + IntToStr(i);
    aButton.Align := alClient;
    aButton.AlignWithMargins := true;
  end;

  GridPanel1.RowCollection.EndUpdate;
  GridPanel1.ColumnCollection.EndUpdate;
end;

So, on the form I have a GridPanel1 and the CreateButtonGrid(3,5) procedure will place 12 buttons in a 3 rows x 4 columns grid, each cell of the same size, buttons filling the cell with a pinch of margin. As you resize the form, buttons grow or shrink but are still evenly distributed in 3×4 grid. Nice!

And two extras:

Get Control at Row/Column

To get the control placed in a specified row / column you can use:

var
  aControl : TControl;
begin
  aControl := GridPanel1.ControlCollection.Controls[2,3]; //3rd row, 4th column
  if Assigned(aControl) AND (aControl IS TButton) then
    TButton(aControl).Caption := 'No hiding in 2,3';
end;

Get Row and Column where Control is

Now, you know a control is somewhere in the grid panel, and you need to know in what row / column:

var
  aControl : TControl;
  idx : integer;
  cRow, cColumn : integer;
begin
  //"pseudo" code (as aControl is nil)

  cRow := -1; cColumn := -1;
  idx := GridPanel1.ControlCollection.IndexOf(aControl);
  if idx > -1 then
  begin
    cRow := GridPanel1.ControlCollection[idx].Row;
    cColumn := GridPanel1.ControlCollection[idx].Column;
  end;
end;

Yes, the TGridPanel can be tricky to use, but when you find a use for it – you will not know how you lived without it 🙂

10 thoughts on “Quick Tip: Dynamically Add Rows, Columns and Controls to Delphi’s TGridPanel

  1. Ali Dehban

    Hi,
    I found the ‘Gridpanel’ very useful.
    Actually the best practice is not ‘ssPercent’ Size Style, and ‘ssabsolute’ style let you optionally and manually determine row/col size and its working very well at run time with a lot of controls on form.

    Reply
  2. Nuzzi

    Yeah, ssPercent is a bit odd. If you set it during run time I have had to put it into a while loop until the value is what you want. Maybe there is a better way, but I have not discovered it.

    Reply
  3. Erik Langendoen

    There is no rowcount or colcount property. When changed that to rowCollection.Count the code still does not work. It looks like TGridPanel is broken in Delphi 10.4

    Reply
    1. Marty

      rowCount and colCount are function parameters:
      procedure TForm1.CreateButtonGrid(const rowCount, colCount : integer);

      Reply
  4. Christian Lindenblatt

    I think in Get Control at Row/Column chapter row an col is reversed. It is the 3rd col and the 4th row.

    Reply
  5. Marty

    for i := 0 to -1 + GridPanel1.ControlCount do
    GridPanel1.Controls[0].Free;

    should be

    for i := 0 to GridPanel1.ControlCount-1 do
    GridPanel1.Controls[i].Free;

    Reply
    1. SiaoLim

      Hi Marty,
      your correction is wrong
      It has to be GridPanel1.Controls[0].Free;
      other vice we will get “List index out of bounds ” at some point as with each iteration list is reduced

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.