(Minimal RepEx at the end)
I have a DBGrid (actually a TSMDBgrid) populated by a query.
One field, payment_made
, is Boolean and displays in the grid as a checkbox either ticked or unticked. depending upon the field value. This is the only field that can be edited. Initially all rows are unticked
I'm using the often published DrawColumnCell() code below to change the whole row colour to blue if the Boolean field on that row gets changed to checked (true) and back to white if it subsequently gets unchecked (false). I want all rows that are checked to remain blue.
The code I use is
procedure TFrmBulkSubdPaymentRecord.SMDBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect;
DataCol: Integer;
Column: TColumn;
State: TGridDrawState);
//see https://www.thoughtco.com/change-coloring-in-tdbgrid-component-4077252
begin
if MyQuery1.FieldByName('payment_made').AsBoolean = true then
SMDBGrid1.Canvas.Brush.Color := $00ffff99//pale blue
else
SMDBGrid1.Canvas.Brush.Color := clwhite;
SMDBGrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;
When starting with a white grid, if I check the payment_made
box in ,say, row 3 the whole row goes blue.
If I check another row, say row 6 then that whole row row goes blue.
If I then uncheck row 3 the whole of that row goes white again
But, if I then check row 3 again, this time just the payment_made
cell goes blue, not the whole row
It looks like could be something to do with the OnDrawColumnCell()
not being called for every cell.
Am I doing this the right way or have I missed something?
I've looked at this post and several other related posts but it appears I am doing it right (although obviously not)
No other processing is done when the field is clicked as it's all done afterwards in a loop that deals with each checked row in turn.
Incidentally, I use
if MyQuery1.FieldByName('payment_made').AsBoolean = true then
simply to make the code more readable. I realise that the = true
is unnecessary and I can live with the extra few microseconds this might add.
Minimal Reproducible example (written in Delphi 2009 and using TSMDBgrid from SM components (although problem seem to exist with the TDGGrid as well)
The .dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 553
ClientWidth = 640
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 344
Top = 56
Width = 61
Height = 13
Caption = '<--- TDBGrid'
end
object Label2: TLabel
Left = 344
Top = 165
Width = 75
Height = 13
Caption = '<--- TSMDBGrid'
end
object DBGrid1: TDBGrid
Left = 8
Top = 16
Width = 321
Height = 97
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
object SMDBGrid1: TSMDBGrid
Left = 8
Top = 137
Width = 321
Height = 104
Options = [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit]
TabOrder = 1
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
Flat = False
BandsFont.Charset = DEFAULT_CHARSET
BandsFont.Color = clWindowText
BandsFont.Height = -11
BandsFont.Name = 'Tahoma'
BandsFont.Style = []
Groupings = <>
GridStyle.Style = gsCustom
GridStyle.OddColor = clWindow
GridStyle.EvenColor = clWindow
TitleHeight.PixelCount = 24
FooterColor = clBtnFace
ExOptions = [eoBooleanAsCheckBox, eoENTERlikeTAB, eoKeepSelection, eoStandardPopup, eoBLOBEditor, eoTitleWordWrap, eoFilterAutoApply]
RegistryKey = 'Software\Scalabium'
RegistrySection = 'SMDBGrid'
WidthOfIndicator = 11
DefaultRowHeight = 24
ScrollBars = ssHorizontal
end
object btnSetTrue: TButton
Left = 480
Top = 111
Width = 75
Height = 25
Caption = 'btnSetTrue'
TabOrder = 2
OnClick = btnSetTrueClick
end
object btnSetFalse: TButton
Left = 480
Top = 160
Width = 75
Height = 25
Caption = 'btnSetFalse'
TabOrder = 3
OnClick = btnSetFalseClick
end
object Memo1: TMemo
Left = 8
Top = 304
Width = 609
Height = 241
Lines.Strings = (
'Top grid is TDBGrid, botton grid is TSMDBGrid'
'Both use DataSource1 as their datasource'
'Both have the same code for their OnDBGrid1DrawColumnCell'
'When the field '#39'payment_made'#39' is TRUE the whole row should be bl' +
'ue, when it is false the whole rowq should be white'
''
'Correct operation'
'=========='
''
'Change the value of the boolean field using the buttons. '
'This operates correctly, the whole row in each grid changes to b' +
'lue or whire correctly. '
'problem evident.'
''
'To reproduce the problem'
'==============='
''
'Change the boolean field from true to false or vice versa using ' +
'either the check box in SMDBGrid or by typing true '
'or false in the DBGrid.'
'Sometimes only one cell on the row changes to the correct colour' +
', leaving other cells on the row the wrong colour.')
TabOrder = 4
end
object ClientDataSet1: TClientDataSet
Aggregates = <>
Params = <>
Left = 376
Top = 240
end
object DataSource1: TDataSource
Left = 448
Top = 240
end
end
The .pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SMDBGrid, Grids, DBGrids, DB, DBClient;
type
TForm1 = class(TForm)
ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
SMDBGrid1: TSMDBGrid;
btnSetTrue: TButton;
btnSetFalse: TButton;
Memo1: TMemo;
Label1: TLabel;
Label2: TLabel;
procedure FormShow(Sender: TObject);
procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure SMDBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure btnSetTrueClick(Sender: TObject);
procedure btnSetFalseClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnSetFalseClick(Sender: TObject);
begin
ClientDataSet1.FieldByName('payment_made').AsBoolean := false;
SMDBGrid1.Refresh ;
DBGrid1.refresh
end;
procedure TForm1.btnSetTrueClick(Sender: TObject);
begin
ClientDataSet1.FieldByName('payment_made').AsBoolean := true;
SMDBGrid1.Refresh ;
DBGrid1.refresh
end;
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if ClientDataSet1.FieldByName('payment_made').AsBoolean = true then
DBGrid1.Canvas.Brush.Color := $00ffff99//pale blue
else
DBGrid1.Canvas.Brush.Color := clwhite;
DBGrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
//define dataset fields
ClientDataSet1.FieldDefs.Add('payment_made', ftBoolean);
ClientDataSet1.FieldDefs.Add('second_column', ftString, 10);
ClientDataSet1.CreateDataSet;
ClientDataSet1.LogChanges := False;
//put in one row of data
ClientDataSet1.Append; // insertfirst row
ClientDataSet1.FieldByName('payment_made').AsBoolean := false;
ClientDataSet1.FieldByName('second_column').AsString := 'row one';
ClientDataSet1.Post;
//leave it in editing mode (although this doesn't seem to make any difference)
ClientDataSet1.Edit;
//set option for SMDBgrid to display booleans as a checkbox
SMDBGrid1.ExOptions := SMDBGrid1.ExOptions + [eoBooleanAsCheckBox] ;
//link components together
DataSource1.DataSet := ClientDataSet1;
SMDBGrid1.DataSource := DataSource1;
DBGrid1.DataSource := DataSource1;
//point the grids to their respective OnDrawColumnCell routines
DBGrid1.OnDrawColumnCell := DBGrid1DrawColumnCell ;
SMDBGrid1.OnDrawColumnCell := SMDBGrid1DrawColumnCell ;
end;
procedure TForm1.SMDBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if ClientDataSet1.FieldByName('payment_made').AsBoolean = true then
SMDBGrid1.Canvas.Brush.Color := $00ffff99//pale blue
else
SMDBGrid1.Canvas.Brush.Color := clwhite;
SMDBGrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;
end.