Nils Eilers, 09.05.2017
Improving Delphi's DBGrid: Automatic Column Width Adjustment
Delphi developers often find that Delphi's DBGrid component lacks certain desired functionalities. One of the most commonly missed features is the automatic adjustment of column widths at the click of a button. This example demonstrates a solution to this problem and shows how the relevant code can be used alongside the column sorting code from the article "Column Sorting with DBGrid" without conflicts.
Like the sorting functionality, this approach is just one of many possible solutions, and the specific requirements may lead to different implementations. This example is written with Delphi XE5 and uses FireDAC components.
Components Used
TDBGrid
TDBNavigator
TFDConnector
TDataSource
TFDQuery
TFDGUIxWaitCursor and TFDPhysMySQLDriverLink (Required for FireDAC with a MySQL database, can be ignored for other use cases)
The TFDConnection component is used to establish the connection to the server. TDBGrid and TDBNavigator are linked with TDataSource, which is connected to TFDQuery, which in turn is linked to TFDConnection.
TFDQuery Configuration
The query for accessing the test database is kept simple:
SELECT *
FROM Artikel;
Adjusting Column Width Automatically
The following code is executed when the OnTitleClick event of the DBGrid is triggered:
procedure TForm1.DBGrid1TitleClick(Column: TColumn);
var
i: Integer;
TmpRecNo: Integer;
NewColumnWidth: Integer;
ColumnTitleWidth: Integer;
ColumnCellWidth: Integer;
ColTitleAdd: Integer;
ColCellAdd: Integer;
ColTitleMult: Real;
ColCellMult: Real;
begin
// Set modifiers for additional column width adjustment
// Column width = Round(Base value * Multiplier) + Addition
ColTitleAdd := 0;
ColTitleMult := 1.10;
ColCellAdd := 0;
ColCellMult := 1.10;
// Adjusting the column width
ColumnTitleWidth := Round(DBGrid1.Canvas.TextExtent(Column.Title.Caption).cx * ColTitleMult) + ColTitleAdd;
ColumnCellWidth := Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColCellMult) + ColCellAdd;
// Finding the widest cell
TmpRecNo := FDQuery1.RecNo;
FDQuery1.First;
FDQuery1.DisableControls;
for i := 0 to FDQuery1.RecordCount - 1 do
begin
if ColumnCellWidth < Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColTitleMult) + ColTitleAdd then
ColumnCellWidth := Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColCellMult) + ColCellAdd;
FDQuery1.Next;
end;
FDQuery1.RecNo := TmpRecNo;
FDQuery1.EnableControls;
// Use the title width if it is wider than the widest cell
if ColumnTitleWidth > ColumnCellWidth then
NewColumnWidth := ColumnTitleWidth
else
NewColumnWidth := ColumnCellWidth;
Column.Width := NewColumnWidth;
end;
Explanation
This code adjusts the column width dynamically by determining the width of the widest cell or the column title.
Modifiers (
ColTitleAdd
,ColTitleMult
,ColCellAdd
,ColCellMult
) allow you to fine-tune the width calculation.The
OnTitleClick
event of the DBGrid is utilized to trigger the resizing.
Preventing Conflicts with Column Sorting
To avoid conflicts between the sorting functionality and the column width adjustment, both are implemented using the OnTitleClick event instead of the OnDblClick event. This is because OnDblClick would trigger OnTitleClick twice, causing undesired behavior.
Solution: Using TTimer Component
By defining a double-click independently from the DBGrid’s default settings, we can ensure compatibility with the sorting code. To achieve this, a TTimer component is required on the form along with two global variables:
private
SingleClick: Boolean = True;
SelectedColumn: TColumn;
These variables are used to transfer data between the OnTitleClick event and the Timer procedure.
Would you like me to proceed with explaining how to implement the timer-based solution to differentiate single-clicks from double-clicks and ensure compatibility with sorting? 😊
If the TTimer is placed on the form, it must be disabled by setting its Enabled
property to False
. The Interval
property determines how quickly the user must click to register a double-click. In this example, an Interval of 200
is chosen. Now, the following lines need to be added to the OnTimer event:
procedure TForm1.Timer1Timer(Sender: TObject); begin SingleClick := true; Timer1.Enabled := false; DBGrid1TitleClick(SelectedColumn); end; |
The combined code from the article dated May 5th, 2017, and the column sorting functionality, taking into account the double-click issue, can now look like this:
procedure TForm1.DBGrid1TitleClick(Column: TColumn); |