How to Use Checkboxes in a DBGrid

Make Your Application More Visually Appealing

Close up of pen and checkbox sign, studio shot
Tetra Images/Getty Images

There are numerous ways and reasons to customize the output of a DBGrid in Delphi. One way is to add checkboxes so that the result is more visually attractive.

By default, if you have a boolean field in your dataset, the DBGrid displays them as "True" or "False" depending on the value of the data field. However, it looks much better if you choose to use a "true" checkbox control to enable editing the fields.

Create a Sample Application

Start a new form in Delphi, and place a TDBGrid, TADOTable, and TADOConnection, TDataSource.

Leave all the component names as they are when they were first dropped into the form (DBGrid1, ADOQuery1, AdoTable1, etc.). Use the Object Inspector to set a ConnectionString property of the ADOConnection1 component (TADOConnection) to point to the sample QuickiesContest.mdb MS Access database.

Connect DBGrid1 to DataSource1, DataSource1 to ADOTable1, and finally ADOTable1 to ADOConnection1. The ADOTable1 TableName property should point to the Articles table (to make the DBGrid display the records of the Articles table).

If you have set all the properties correctly, when you run the application (given that the Active property of the ADOTable1 component is True) you should see, by default, the DBGrid display the boolean field's value as "True" or "False" depending on the value of the data field.

CheckBox in a DBGrid

To show a checkbox inside a cell of a DBGrid, we'll need to make one available for us at run time.

Select the "Data controls" page on the Component Palette and pick a TDBCheckbox. Drop one anywhere on the form - it doesn't matter where, since most of the time it will be invisible or floating over the grid.

Tip: TDBCheckBox is a data-aware control that allows the user to select or deselect a single value, which is appropriate for boolean fields.

Next, set its Visible property to False. Change the Color property of DBCheckBox1 to the same color as the DBGrid (so it blends in with the DBGrid) and remove the Caption.

Most importantly, make sure the DBCheckBox1 is connected to the DataSource1 and to the correct field.

Note that all the above DBCheckBox1's property values can be set in the form's OnCreate event like this:

procedure TForm1.FormCreate(Sender: TObject);
begin
 DBCheckBox1.DataSource := DataSource1;
 DBCheckBox1.DataField := 'Winner';
 DBCheckBox1.Visible := False;
 DBCheckBox1.Color := DBGrid1.Color;
 DBCheckBox1.Caption := '';
 
 //explained later in the article
 DBCheckBox1.ValueChecked := 'Yes a Winner!'; 
 DBCheckBox1.ValueUnChecked := 'Not this time.'; 
end;

What comes next is the most interesting part. While editing the boolean field in the DBGrid, we need to make sure the DBCheckBox1 is placed above ("floating") the cell in the DBGrid displaying the boolean field.

For the rest of the (non-focused) cells carrying the boolean fields (in the "Winner" column), we need to provide some graphical representation of the boolean value (True/False).

This means you need at least two images for drawing: one for the checked state (True value) and one for the unchecked state (False value).

The easiest way to accomplish this is to use the Windows API DrawFrameControl function to draw directly on the DBGrid's canvas.

Here's the code in the DBGrid's OnDrawColumnCell event handler that occurs when the grid needs to paint a cell.

procedure TForm1.DBGrid1DrawColumnCell(
 Sender: TObject; const Rect: TRect; DataCol: 
 Integer; Column: TColumn; State: TGridDrawState);
 
const IsChecked : array[Boolean] of Integer = 
 (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED);
var
 DrawState: Integer;
 DrawRect: TRect;
begin
 if (gdFocused in State) then
 begin
 if (Column.Field.FieldName = DBCheckBox1.DataField) then
 begin
 DBCheckBox1.Left := Rect.Left + DBGrid1.Left + 2;
 DBCheckBox1.Top := Rect.Top + DBGrid1.top + 2;
 DBCheckBox1.Width := Rect.Right - Rect.Left;
 DBCheckBox1.Height := Rect.Bottom - Rect.Top;

 DBCheckBox1.Visible := True;
 end
 end
 else
 begin
 if (Column.Field.FieldName = DBCheckBox1.DataField) then
 begin
 DrawRect:=Rect;
 InflateRect(DrawRect,-1,-1);

 DrawState := ISChecked[Column.Field.AsBoolean];

 DBGrid1.Canvas.FillRect(Rect);
 DrawFrameControl(DBGrid1.Canvas.Handle, DrawRect, 
 DFC_BUTTON, DrawState);
 end;
 end; 
end;

To finish this step, we need to make sure DBCheckBox1 is invisible when we leave the cell:

procedure TForm1.DBGrid1ColExit(Sender: TObject);
begin
 if DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField then 
 DBCheckBox1.Visible := False
end;

We need just two more events to handle.

Note that when in editing mode, all keystrokes are going to the DBGrid's cell, we have to make sure they are sent to the CheckBox. In the case of a CheckBox we are primarily interested in the [Tab] and the [Space] key. [Tab] should move the input focus to the next cell, and [Space] should toggle the state of the CheckBox.

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);
begin
 if (key = Chr(9)) then Exit;

 if (DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField) then
 begin
 DBCheckBox1.SetFocus;
 SendMessage(DBCheckBox1.Handle, WM_Char, word(Key), 0);
 end;
end;

It could be appropriate for the Caption of the checkbox to change as the user checks or unchecks the box. Note that the DBCheckBox has two properties (ValueChecked and ValueUnChecked) used to specify the field value represented by the checkbox when it is checked or unchecked.

This ValueChecked property holds "Yes, a Winner!", and ValueUnChecked equals "Not this time."

procedure TForm1.DBCheckBox1Click(Sender: TObject);
begin
 if DBCheckBox1.Checked then
 DBCheckBox1.Caption := DBCheckBox1.ValueChecked
 else
 DBCheckBox1.Caption := DBCheckBox1.ValueUnChecked;
end;

Run the project and you'll see the checkboxes all over the Winner field's column.