How to adjust TDBGrid column widths automatically

By: Philippe Randour

Abstract: This handy procedure automatically adjusts the size of static DBGrid columns to fit the DBGrid client width when the user resizes the form containing the grid. Say goodbye to the horizontal scrollbar! By Philippe Randour.

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!


Server Response from: ETNASC04