1. Computing & Technology

Discuss in my forum

How to AutoFit the Columns in the TDBGrid

Adjusting Column Widths to Fit the Widest Entry in a DBGrid Column

By , About.com Guide

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:

  1. Handling the OnDblClick event of the DBGrid for Column Titles,
  2. Finding the double-clicked Column,
  3. Calculating the widest entry and setting the Column Width.
Note: To hold the reference to the column being selected for "autofit" a form-level record type variable is required, defined as:
 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!

©2012 About.com. All rights reserved.

A part of The New York Times Company.