Вы находитесь на странице: 1из 9

CheckBox in a DBGrid!

Or, better to say, a DBCheckBox in a DBGrid.


Ok, here we go. To show a check box 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.
TDBCheckBox is a data-aware control that allows the user to select or deselect a
single value - most 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. And most importantly, make sure the DBCheckBox1 is connected to the
DataSource1 and to the correct field (DataSource = DataSource1, DataField =
Winner).

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

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 (True value) state, and one for
the unchecked (False value) state. 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;
And finally, the last touch. 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 check box when it is checked / unchecked. My 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;

Drop down list (DBLookupComboBox) inside a DBGrid

procedure TForm1.FormCreate(Sender: TObject);


begin
with DBLookupComboBox1 do
begin
DataSource := DataSource1; // -> AdoTable1 -> DBGrid1
ListSource := DataSource2;
DataField := 'AuthorEmail'; // from AdoTable1 - displayed in the DBGrid
KeyField := 'Email';
ListFields := 'Name; Email';

Visible

:= False;

end;

DataSource2.DataSet := AdoQuery1;

AdoQuery1.Connection := AdoConnection1;
AdoQuery1.SQL.Text := 'SELECT Name, Email FROM Authors';
AdoQuery1.Open;
end;
Note: when you want to display more than one field in a DBLookupComboBox, as in
the example above, you have to make sure that all columns are visible. This is done
by setting the DropDownWidth property. However, you'll see that initially you have
to set this to a very large value which results in dropped list being too wide in most
cases. One workaround is to set the DisplayWidth of a particular Field shown in a
drop down list. The next code snippet, placed inside the OnCreate event for the
form, ensures that both author name and it's email are displayed inside the drop
down list:
AdoQuery1.FieldByName('Email').DisplayWidth:=10;
AdoQuery1.FieldByName('Name').DisplayWidth:=10;
AdoQuery1.DropDownWidth:=150;
Magic...

What's left for us to do, is to actually make a combo box hover over a cell (when in
edit mode) displaying the AuthorEmail field. We've already discussed the theory - I'll
show you only the code here (you'll have the option to download the entire project
later):
First, we need to make sure the DBLookupComboBox1 is moved (and sized) over the
cell in which the AuthorEmail field is displayed.

procedure TForm1.DBGrid1DrawColumnCell
(Sender: TObject;
const Rect: TRect;
DataCol: Integer;
Column: TColumn;
State: TGridDrawState);
begin
if (gdFocused in State) then
begin
if (Column.Field.FieldName = DBLookupComboBox1.DataField) then
with DBLookupComboBox1 do
begin
Left := Rect.Left + DBGrid1.Left + 2;
Top := Rect.Top + DBGrid1.Top + 2;
Width := Rect.Right - Rect.Left;
Width := Rect.Right - Rect.Left;
Height := Rect.Bottom - Rect.Top;

Visible := True;
end;
end
end;
Next, when we leave the cell, we have to hide the combo box:

procedure TForm1.DBGrid1ColExit(Sender: TObject);


begin
if DBGrid1.SelectedField.FieldName = DBLookupComboBox1.DataField then
DBLookupComboBox1.Visible := False
end;
Next, 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 DBLookupComboBox. In the case of a
DBLookupComboBox we are primarily interested in the [Tab] key - [Tab] should
move the input focus to the next cell.

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);


begin
if (key = Chr(9)) then Exit;

if (DBGrid1.SelectedField.FieldName = DBLookupComboBox1.DataField) then


begin
DBLookupComboBox1.SetFocus;
SendMessage(DBLookupComboBox1.Handle, WM_Char, word(Key), 0);
end
end;
That's it. Run the project and voila ... one nicely looking drop down list box (with two
columns!) all over the AuthorEmail field's column.
What does happen when you select a value (Name; Email) from the drop down list?
When you pick an item ("row") from a DBLookupComboBox, the value or the
corresponding KeyField field is stored as the value of the DataField field.

DateTimePicker in a DBGrid!

procedure TForm1.DBGrid1DrawColumnCell
(Sender: TObject;
const Rect: TRect;
DataCol: Integer;
Column: TColumn;
State: TGridDrawState);
begin
if (gdFocused in State) then
begin
if (Column.Field.FieldName = 'DateAdded') then
with DateTimePicker do
begin
Left := Rect.Left + DBGrid1.Left + 1;
Top := Rect.Top + DBGrid1.Top + 1;
Width := Rect.Right - Rect.Left + 2;
Width := Rect.Right - Rect.Left + 2;
Height := Rect.Bottom - Rect.Top + 2;

Visible := True;
end;
end
end;
Next, when we leave the cell, we have to hide the date time picker:

procedure TForm1.DBGrid1ColExit(Sender: TObject);


begin
if DBGrid1.SelectedField.FieldName = 'DateAdded' then
DateTimePicker.Visible := False
end;

Next, 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 DateTimePicker. We are primarily
interested in the [Tab] key - [Tab] should move the input focus to the next cell.

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

if (DBGrid1.SelectedField.FieldName = 'DateAdded') then


begin
DateTimePicker.SetFocus;
SendMessage(DateTimePicker.Handle, WM_Char, word(Key), 0);
end
end;
We are not finished yet. Just two more events to handle! What we need to do is to
place the dataset into edit mode when the user opens the drop-down calendar, and
we have to assign the value of the date that is marked on the calendar to the
DateAdded field. Just handle the OnDropDown and the OnChange events:

procedure TForm1.DateTimePickerChange(Sender: TObject);


begin
if DBGrid1.DataSource.State in [dsEdit, dsInsert] then
ADOTable1DateAdded.Value := DateTimePicker.DateTime;
end;

procedure TForm1.DateTimePickerDropDown(Sender: TObject);


begin
DBGrid1.DataSource.Edit;
end;
That's it. Run the project and voila ... one nicely looking drop down calendar
enabling you to change the value of DateAdded field's column.