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 🙂
How to use >>> Get Control at Row/Column
HI, the same code is the “how”.
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.
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.
Hello,
Thanks for information,
How can we place the list in AdoQuery? (AdoQuery PRODUCT_NAME.text)
How do we run the tenth event?
Thank you.
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
rowCount and colCount are function parameters:
procedure TForm1.CreateButtonGrid(const rowCount, colCount : integer);
I think in Get Control at Row/Column chapter row an col is reversed. It is the 3rd col and the 4th row.
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;
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