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