Delphi DBGrid alternate row colors for all DBGrids in the project

14,349

Solution 1

If you put something like this in your datamodule, and assign it to the OnDrawColumnCell of every DBGrid, it seems to work (see notes that follow):

procedure TDataModule1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
  RowColors: array[Boolean] of TColor = (clSilver, clDkGray);
var
  OddRow: Boolean;
begin
  // Safety check, although it really isn't needed; no other control accepts
  // this event handler definition, AFAIK, so the only way to call it with the
  // wrong Sender type would be to do so in your own code manually. In my own
  // code, I'd simply leave out the check and let the exception happen; if I
  // was stupid enough to do so, I'd want my hand slapped rudely.
  if (Sender is TDBGrid) then
  begin
    OddRow := Odd(TDBGrid(Sender).DataSource.DataSet.RecNo);
    TDBGrid(Sender).Canvas.Brush.Color := RowColors[OddRow];
    TDBGrid(Sender).DefaultDrawColumnCell(Rect, DataCol, Column, State);
  end;
end;

A couple of notes:

  • First, you should avoid using TDataSet.RecNo in the first place, because post-BDE datasets don't typically have this value available. Accessing it (particularly on large or query-based datasets) causes a major performance hit to your application. Of course, not using it means that you can't use this solution. A better solution would be to use a handler for the dataset's BeforeScroll or AfterScroll event that toggled a boolean available to this code instead, and use that instead of the test for Odd(RecNo), or if the dataset is only used for displaying in the DBGrid, use the TDataSet.Tag in the AfterScroll event to track the row's odd/even state using

    OddRow := Boolean(DataSet.Tag);
    DataSet.Tag := Ord(not OddRow);
    
  • Add DBGrids to the uses clause of your datamodule, and manually declare the above event in the published section so that it's available to all units that use the datamodule. You can then assign it in the Object Inspector Events tab as usual from those units.

  • This does not properly handle the TGridDrawState (nor does your initial code). You'll need to add handling for that yourself, as that wasn't what you asked here.

  • Depending on which color you want for odd and even rows, you may want to reverse the order of the colors in RowColors.

  • I prefer the repeated typecasts so that it's clear what the code is doing. If it bothers you, you can simply declare a local variable instead:

    var
      OddRow: Boolean;
      Grid: TDBGrid;
    begin
      if (Sender is TDBGrid) then
      begin
        Grid := TDBGrid(Sender);
        OddRow := Odd(Grid.DataSource.DataSet.RecNo);
        ...
      end;
    end;
    

Solution 2

This works for Delphi XE7

type
  TDBGrid=Class(Vcl.DBGrids.TDBGrid)
    procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  end;

procedure TDBGrid.WMVScroll(var Message: TWMVScroll);
begin
  Self.Invalidate;
  inherited;
end;

procedure TForm1. DBGrid1MouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  if Sender is TDBGrid then
    (Sender as TDBGrid).Invalidate;
end;

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
  MyRowColors : array[Boolean] of TColor = (clLime, clMoneyGreen);
var
  RowNo  : Integer;
  OddRow : Boolean;
  S      : string;
begin
  if Sender is TDBGrid then begin
    with (Sender as TDBGrid) do  begin
      if (gdSelected in State) then begin
        // Farbe für die Zelle mit dem Focus
        // color of the focused row
        Canvas.Brush.Color := clblue;
      end   
      else  begin
        // count := trunc((Sender as TDBGrid).Height div (Rect.Bottom - Rect.Top));
        // RowNo := (Sender as TDBGrid).Height div Rect.Top;
        RowNo := Rect.Top div (Rect.Bottom - Rect.Top);
        OddRow := Odd(RowNo);
        Canvas.Brush.Color := MyRowColors[OddRow];

        // Font-Farbe immer schwarz
        // font color always black
        Canvas.Font.Color := clBlack;
        Canvas.FillRect(Rect);
        // Denn Text in der Zelle ausgeben
        // manualy output the text
        if Column.Field <> nil then begin
          S := Column.Field.AsString;
          Canvas.TextOut(Rect.Left + 2, Rect.Top + 1, S);
          //  Canvas.TextOut(Rect.Left + 2, Rect.Top + 1, 'Column.Field.AsString');
        end;
      end;
    end 
  end; 
end;
Share:
14,349
user1137313
Author by

user1137313

Updated on June 04, 2022

Comments

  • user1137313
    user1137313 almost 2 years

    How can I make all my grids look the same way all over my forms? I want to implement an alternate row color that must be applied on all grids of my project. Is it possible without adding the same DrawColumnCell event code for every grid? I want to avoid adding the same code for each of my grids. I have like 30 grids in my project and multiplied by 13 rows of code it just adds a lot of code lines to my project making it "unfriendly". I am looking for a solution that will only add 13 lines of code to the project, not 390 lines.

    My formatting code looks like this (for example):

    procedure TDBGrid.DBGrid1DrawColumnCell(Sender: TObject;const Rect: TRect;DataCol: Integer;Column: TColumn;State: TGridDrawState) ;
    var
       grid : TDBGrid;
       row : integer;
    begin
       grid := sender as TDBGrid;
       row := grid.DataSource.DataSet.RecNo;
       if Odd(row) then
         grid.Canvas.Brush.Color := clSilver
       else
         grid.Canvas.Brush.Color := clDkGray;
       grid.DefaultDrawColumnCell(Rect, DataCol, Column, State) ;
    end;
    

    Probably I would need to extend the DBGrid somehow, but I do not know exactly how nor how to look for a solution for this on google

    I tried to hack the DBGRid inside each form like this:

    type
      TDBGrid = class(DBGrids.TDBGrid)
      protected
        procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;Column: TColumn; State: TGridDrawState); override;
      end;
    ...
    procedure TDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;Column: TColumn; State: TGridDrawState) ;
    var
           grid : TDBGrid;
           row : integer;
    begin
           row := 2;//grid.DataSource.DataSet.RecNo;
           if Odd(row) then
             Canvas.Brush.Color := clSilver
           else
             Canvas.Brush.Color := clDkGray;
           DefaultDrawColumnCell(Rect, DataCol, Column, State) ;
    end;
    

    I can do this but I cannot access the sender, so I can access the dataset and know which record to color and which not (odd and even). And this is a poor approach anyways since I will have to do it on every form, so it's not really a solution

    Any ideas?

    Thank you