Users accustomed to working with Microsoft Excel operate on data displayed in rows and columns. One of the Excel formatting features is the ability to automatically change the width of a column to fit the contents by double-clicking on the boundary to the right of a selected column title.
Data manipulation using Microsoft Excel is similar to using Delphi's TDBGrid in database applications - where DBGrid is used to display and edit data from any kind of (supported) data source.
AutoFit for DBGrid Columns
Let's see how to add the "AutoFit" functionality to the Columns of a TDBGrid component. The discussion that follows explains adding Auto-Fit on DBGrid's Column Title Double-Click event.Note: to follow along, create a new Delphi project, drop a TDBGrid component (named "DBGrid1") on a form ("Form1") and setup any kind of data source to be displayed in the grid.
In most cases a column in a DBGrid is "connected" to a field in a dataset (through the DataSource property) - thus displaying the field's data for a particular dataset's "row". The width of a particular column in a DBGrid can be set at design-time using the Columns Editor.
By default, a user can customize (at run-time) the width and the order of the columns by dragging and dropping column titles.
To prevent a user from rearranging columns at run-time, you can either remove the goColResizing flag from the Options property or set the DragMode property to dmAutomatic (dmManual by default).
Note: here's how to allow column resize but disable movement
Here's how to to automatically fix the size of TDBGrid columns (at run-time) to fit the DBGrid width (remove the unfilled space at the right edge of the grid; and consequently remove the horizontal scroll bar) when the user resizes the container containing the grid.
Here's how to Store and Retrieve DBGrid's Columns Order and Visibility
Adding "AutoFit" to DBGrid involves solving 3 problems:
- Handling the OnDblClick event of the DBGrid for Column Titles,
- Finding the double-clicked Column,
- Calculating the widest entry and setting the Column Width.
type
TColumnWidthHelper = record
Index : integer;
MaxWidth : integer;
end;
The "Index" field holds the index of the column in the Columns array. MaxWidth stores the width of the widest cell for the current "view".
Column Title DoubleClicked?
First we need to react on the Column Title double click event. While DBGrid exposes the OnTitleClick that fires on a single click, we cannot use it as we are interested in double-clicks :(
The OnDblClick event looks promising - unfortunately it will fire even if user double clicks anywhere inside the Grid.
Here's how to handle the OnDblClick event and make sure the double-click has occurred on a Column Title:
procedure TForm1.DBGrid1DblClick(Sender: TObject) ;
var
mouseInGrid : TPoint;
gridCoord: TGridCoord;
begin
//Convert "Screen" Mouse to "Grid" Mouse
mouseInGrid := DBGrid1.ScreenToClient(Mouse.CursorPos) ;
gridCoord := DBGrid1.MouseCoord(mouseInGrid.X, mouseInGrid.Y) ;
//Column titles NOT displayed?
if not (dgTitles in DBGrid1.Options) then Exit;
//Titles displayed but we double-clicked on "regular" row?
if gridCoord.Y <> 0 then Exit;
//find Column index
if dgIndicator in DBGrid1.Options then
ColumnWidthHelper.Index := -1 + gridCoord.x
else
ColumnWidthHelper.Index := gridCoord.x;
//Indicator Column?
if ColumnWidthHelper.Index < 0 then Exit;
... // continues below
When we are sure that a column title was double clicked we can find the widest entry in that column and set column width ...
//continues from above ...
ColumnWidthHelper.MaxWidth := -1;
//"recalculate" ColumnWidthHelper.MaxWidth
DBGrid1.Repaint;
//"auto size" Column width
DBGrid1.Columns[ColumnWidthHelper.Index].Width := 4 + ColumnWidthHelper.MaxWidth;
end;
The trick is in the DBGrid1.Repaint call. This forces the grid to repaint itself thus fireing the DrawColumnCell event (for all the visible rows).
procedure TForm1.DBGrid1DrawColumnCell(
Sender: TObject;
const Rect: TRect;
DataCol: Integer;
Column: TColumn;
State: TGridDrawState) ;
begin
//is this is the column we want to auto-size?
if DataCol = ColumnWidthHelper.Index then
begin
//Column has field?
if Assigned(Column.Field) then
begin
//find the widest string
ColumnWidthHelper.MaxWidth := Max(ColumnWidthHelper.MaxWidth, DBGrid1.Canvas.TextWidth(Column.Field.DisplayText)) ;
end;
end;
end;
That's it! AutoFit in DBGrid!

