delphidelphi-10.2-tokyotdbgrid

TDBGrid: OnDrawColumnCell Data is overlapping


I am working on Delphi 10.2 and SQL Server 2008.

I have to modify some value in TDBGrid. when I modify the value using OnDrawColumnCell Data is getting over lapped when I click on that column and the same is working fine in Delphi 7.

enter image description here

Example Code:

Create table and insert some data in SQL Server 2008.

CREATE TABLE [dbo].[Persons](
    [P_ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [LastName] [varchar](15) NOT NULL,
)
insert into Persons (LastName) values ('LastName')

Create New VCL Forms Application - Delphi

To display the Data on DBGrid I am using TADOCOnnection, TADOQuery, TDataSource and TDBGrid

set TADOQuery.SQL to "select LastName from Persons"

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, DBGrids, DB, ADODB, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    procedure FormShow(Sender: TObject);
    procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormShow(Sender: TObject);
begin
  ADOQuery1.Active := False;
  ADOQuery1.Active := True;
end;

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
Var
  CellData : String;
begin
  CellData := Column.Field.DisplayText;

  if Column.Field.FieldName = 'LastName' then
  begin
    CellData := 'change';
    DBGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, CellData);
  end
  else
    DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, state);
end;

end.

Solution

  • This is a general drawing issue and not related to SQL Server or the TDBGrid in particular. The same would apply to drawing to a VCL TCanvas or something similar.

    Clear the area before drawing text

    You are calling Canvas.TextRect(..), but nothing more. The text will get drawn, but nothing more. You will have to clear the area first: Imagine painting a white background before drawing black text.

    The TDBGrid offers a convenient method DrawCellBackground(..). Since this method is not public, this screams for implementing it by leveraging helper classes.

    Implementation example

    The following code uses DrawCellHighlight(..) for clearing the cell paint area when the cell is selected and DrawCellBackground(..) otherwise.

    type
        TDBGridHelper = class helper for TDBGrid
            public const textPaddingPx = 2; // Siehe TDBGrid::DefaultDrawColumnCell
            public procedure writeText(
                const   inRect:         TRect;
                const   text:           String;
                const   State:          TGridDrawState;
                const   columnIndex:    Integer
            );
        end;
    
    procedure TDBGridHelper.writeText(
        const   inRect:         TRect;
        const   text:           String;
        const   State:          TGridDrawState;
        const   columnIndex:    Integer
    );
    const
        cellGridPx = 1;
    var
        backgroungRect: TRect;
    begin
        backgroungRect := inRect;
        backgroungRect.Inflate(-cellGridPx, -cellGridPx);
    
        if (Vcl.Grids.gdSelected in State) then
            DrawCellHighlight(inRect, State, columnIndex, 0)
        else
            DrawCellBackground(backgroungRect, self.Color, State, Columnindex, 0);
    
        Canvas.TextRect(
            inRect,
            inRect.Left + textPaddingPx,
            inRect.Top + textPaddingPx,
            text
        );
    end;
    

    Leveraging the TDBGrid.OnDrawColumnCell event was absolutely correct, you can now simplify it to something like

    procedure TYourFrame.dbGridDrawColumnCell(
        Sender: TObject;
        const Rect: TRect;
        DataCol: Integer;
        Column: TColumn;
        State: TGridDrawState
    );
    var
        columnText:     String;
    begin
        columnText := '---';
        if Assigned(Column.Field) then begin
            if (Column.FieldName = 'yourField') then
                columnText := getDeviationColumnText(Column.Field.AsSingle)
            else
                // This is the default text
                columnText := Column.Field.DisplayText; 
        end;
        (Sender as TDBGrid).writeText(Rect, columnText, State, Column.Index);
    end;