Among the most useful controls on the VCL palette is the TDBGrid component.
But just as nobody is perfect, neither is any component as flexible and useful
as it could and should be. So is it with TDBGrid. Thanks to the power of
object-oriented programming, however, we needn't live with the stock TDBGrid. We
can consider it a starting
point for more advanced grids.
One of the annoying characteristics of TDBGrid is that there is no option to
automatically adjust static columns defined at design-time to completely fit the grid client
area. This would make applications look more professional , especially if the grid can be resized at run-time.
In this article, we will build a procedure that remedies this deficiency. The
procedure will let you define the
general layout of the grid at design-time by creating static columns for the grid,
confident that proportions between columns will be maintained at run-time regardless
of whether the user resizes the grid.
To enable this new feature, disable
column sizing for the grid (dgColSizing set to False in the grid options) and make a call to
the new procedure in the OnResize event of the form holding the grid. It's s that simple.
How does it work?
Procedure AdjustColumnWidths starts by computing the total width used by grid columns,
plus the width of vertical lines if they are included
in the grid options (dgColLines set to True). These lines are one pixel wide.
Then it computes the grid client width. There are several parameters to take into account:
- The vertical scroll bar. Its width is computed through a standard API call. We will assume that
the scroll bar is always visible, thus keeping the same layout regardless of the record count of the
dataset linked to the grid.
- The grid indicator. Its width is defined as a const in DBGrids.pas. If vertical lines are included
in the grid options, you have to take them into consideration because the indicator appears in a fixed column.
- BorderStyle and Ctl3D. With BorderStyle set to bsSingle, you can have two different borders:
a sunken border if Ctl3D is True and a one-dimensional border if Ctl3D is False.
Finally, the code equally distributes among columns the difference between the total column width
and the grid client width.
Let's see some code!
{ This unit was developed by Philippe Randour (philippe_randour@hotmail.com)
in August 2000. It can be freely used in your own development.
Thank you for your interest. }
unit AdjustGrid;
interface
uses Windows, Forms, DBGrids;
procedure AdjustColumnWidths(DBGrid: TDBGrid);
implementation
procedure AdjustColumnWidths(DBGrid: TDBGrid);
var
TotalColumnWidth, ColumnCount, GridClientWidth, Filler, i: Integer;
begin
ColumnCount := DBGrid.Columns.Count;
if ColumnCount = 0 then
Exit;
// compute total width used by grid columns and vertical lines if any
TotalColumnWidth := 0;
for i := 0 to ColumnCount-1 do
TotalColumnWidth := TotalColumnWidth + DBGrid.Columns[i].Width;
if dgColLines in DBGrid.Options then
// include vertical lines in total (one per column)
TotalColumnWidth := TotalColumnWidth + ColumnCount;
// compute grid client width by excluding vertical scroll bar, grid indicator,
// and grid border
GridClientWidth := DBGrid.Width - GetSystemMetrics(SM_CXVSCROLL);
if dgIndicator in DBGrid.Options then begin
GridClientWidth := GridClientWidth - IndicatorWidth;
if dgColLines in DBGrid.Options then
Dec(GridClientWidth);
end;
if DBGrid.BorderStyle = bsSingle then begin
if DBGrid.Ctl3D then // border is sunken (vertical border is 2 pixels wide)
GridClientWidth := GridClientWidth - 4
else // border is one-dimensional (vertical border is one pixel wide)
GridClientWidth := GridClientWidth - 2;
end;
// adjust column widths
if TotalColumnWidth < GridClientWidth then begin
Filler := (GridClientWidth - TotalColumnWidth) div ColumnCount;
for i := 0 to ColumnCount-1 do
DBGrid.Columns[i].Width := DBGrid.Columns[i].Width + Filler;
end
else if TotalColumnWidth > GridClientWidth then begin
Filler := (TotalColumnWidth - GridClientWidth) div ColumnCount;
if (TotalColumnWidth - GridClientWidth) mod ColumnCount <> 0 then
Inc(Filler);
for i := 0 to ColumnCount-1 do
DBGrid.Columns[i].Width := DBGrid.Columns[i].Width - Filler;
end;
end;
end.
Where to go now?
The obvious next step is to create a TDBGrid descendant that incorporates
this new functionality.
But this is another story -- I'll leave it as an exercise for the reader!