How to change TDBGrld row colour when a field value is changed

648 views Asked by At

(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.
0

There are 0 answers