Jump to content
Squall_FF8

tDBGrid: how to color selected row?

Recommended Posts

Hey guys,

I have a tDBGrid that I try to paint the selected row similar to the picture bellow. May you advise/share a snippet of  how to do it?

dgRowSelect is false!

 

image.png

image.png

Edited by Squall_FF8

Share this post


Link to post

There is no easy way to do it. Problem is that this feature is not supported "by design" in TDBGrid.
Main issue is in this part of TCustomDBGrid.DrawCell:    

 Value := '';
      OldActive := FDataLink.ActiveRecord; 
      try
        FDataLink.ActiveRecord := ARow; //<-- after this line, current cell is at current row anyway
        if Assigned(DrawColumn.Field) then
          Value := DrawColumn.Field.DisplayText;
        if HighlightCell(ACol, ARow, Value, AState) and DefaultDrawing then
          DrawCellHighlight(ARect, AState, ACol, ARow);
        if not Enabled then
          Font.Color := clGrayText;
        if FDefaultDrawing then
          WriteText(Canvas, ARect, 3, 2, Value, DrawColumn.Alignment,
            (DrawColumn.BidiMode = bdRightToLeft) and
            UseRightToLeftAlignmentForField(DrawColumn.Field, DrawColumn.Alignment));
        if Columns.State = csDefault then
          DrawDataCell(ARect, DrawColumn.Field, AState); //<-- here we have event
        DrawColumnCell(ARect, ACol, DrawColumn, AState); //<-- here we have event
      finally
        FDataLink.ActiveRecord := OldActive; //<-- after this code, active row at right place again
      end;

So when you try to check active row in event it's always return true.

right way to check if current cell is in active row it's:

if OldActive = FDataLink.ActiveRecord then

but OldActive it's local variable so you don't have access to it.

Only way to deal with it - it's make own descendant of TCustomDBGrid and reimplement DrawCell method.
 

  • Like 1

Share this post


Link to post
3 hours ago, Softacom | Company said:

There is no easy way to do it. Problem is that this feature is not supported "by design" in TDBGrid.

So that is why I couldn't find the answer in my google search ...
For me the problem is twofold: 
- Row/Col are private
- no Row info in DrawColumnCell
if we had that (which is quite reasonable for me), it is a matter of simple comparison.

BYW Let say I implement a descendant and call it TMyGrid, is there an easy way to replace the TDBGrid in existing project?
I tried to manually replace "Grid: TDBGrid;" in the source but it didnt work

Share this post


Link to post
7 minutes ago, Squall_FF8 said:

I tried to manually replace "Grid: TDBGrid;" in the source but it didnt work

Have you replaced in both sources? .pas and .dfm?

Share this post


Link to post
25 minutes ago, Cristian Peța said:

Have you replaced in both sources? .pas and .dfm?

Only in the .pas. I cant find in Delhi12 how to view the .dfm  as text 

EDIT: found it and it works, Thanks!!!

Edited by Squall_FF8

Share this post


Link to post
3 minutes ago, Squall_FF8 said:

how to view the .dfm  as text

Right-click on the form and in contextual menu select "View as text"

EDIT: or Alt-F12 to switch between.

Edited by Cristian Peța

Share this post


Link to post

Yes, it’s not so hard to replace component. You need to follow next steps:

1.       Create package with your new component and install it into Delphi (don’t forget to make package DesingnTime and make register procedure). We need it to make designer works.

2.       Replace text ': TDBGrid'#13#10 with ': TMyGrid'#13#10 in *.dfm files

3.       Replace text ': TDBGrid;'#13#10 with ': TMyGrid;'#13#10 in *.pas files

4.       We need to add your pas-file with new type into interface uses section to let compiler find you new class. You can do it in a few ways, but I suggest, replace text ‘Vcl.DBGrids’ with ‘Vcl.DBGrids, MyNewGrid’ in *.pas files

5.       If your project not single exe-file and use own bpl-files with this new grid, than you need to add new package into requires section.

6.       If your project cannot compile after all this steps – maybe you will need to add path for this new pas-file into “search path” section in options of your project

Edited by Softacom | Company
  • Like 1

Share this post


Link to post
8 minutes ago, Softacom | Company said:

2.       Replace text ‘: TDBGrid#13#10’ with ‘: TMyGrid#13#10’ in *.dfm files

3.       Replace text ‘: TDBGrid;#13#10’ with ‘: TMyGrid;#13#10’ in *.pas files

That part is quite hacky ... When I change in the form and try to go back to pas, I get error in the IDE. The project compiles and works well, but I need to save first, close the project and then open it again

Share this post


Link to post
1 minute ago, Squall_FF8 said:

That part is quite hacky ... When I change in the form and try to go back to pas, I get error in the IDE. The project compiles and works well, but I need to save first, close the project and then open it again

Yes, all text replaces must be done in same time (for IDE). That's why you need to do replace with some other tools, not in IDE it's self.
You can use Notepad++ for example.
Just don't forget adopt syntax of replacing for this tool (for example #13 in Delphi, in Notepad++ it's /n)

Share this post


Link to post
56 minutes ago, Squall_FF8 said:

That part is quite hacky ... When I change in the form and try to go back to pas, I get error in the IDE.

First in pas then in dfm

Share this post


Link to post

I'm using this code for alternating colors:

Procedure TDBGrid.DrawColumnCell(Const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
Var
  dataset: TDataSet;
  {$IFDEF HIDECOLLINESIFEMPTY}
  colline: Boolean;
  {$ENDIF}
  editcolor: TColor;
  hidefocus: Boolean;
Begin
  dataset := Self.DataSource.DataSet;

  // This method is only being called on DATA cells, which only happens if there is a dataset connected. Therefore, no
  // need to perform an assigned check here.

  {$IFDEF HIDECOLLINESIFEMPTY}
  colline := True;
  {$ENDIF}
  hidefocus := Not (csDesigning In Self.ComponentState) And (gdSelected In State) And Not Self.Focused;

  If (dgMultiSelect In Self.Options) And (Self.SelectedRows.CurrentRowSelected) Then
  Begin
  End
  Else
  If dataset.IsEmpty Then
  Begin
    {$IFDEF HIDECOLLINESIFEMPTY}
    colline := False;
    {$ENDIF}
    editcolor := TStyleManager.ActiveStyle.GetStyleColor(scEdit);
    Self.Canvas.Brush.Color := editcolor;
    Self.Canvas.Font.Color := editcolor;
  End
  Else
// This code imitates the highlight of the whole row even if RowSelect is disabled. Note that it needs MultiSelect to be enabled!
//  If Not (gdSelected In State) And grid.SelectedRows.CurrentRowSelected Then
//    grid.Canvas.Brush.Color := clHighLight
//  Else
  If (dataset.RecNo Mod 2 = 0) And ((State = []) Or hidefocus) Then
    Self.Canvas.Brush.Color := TStyleManager.ActiveStyle.GetStyleColor(scButtonDisabled)
  Else If (dataset.RecNo Mod 2 = 1) And hidefocus Then
    Self.Canvas.Brush.Color := TStyleManager.ActiveStyle.GetStyleColor(scEdit);

  If hidefocus Then
    Self.Canvas.Brush.Color := TStyleManager.ActiveStyle.GetStyleColor(scCategoryButtonsGradientBase);

  {$IFDEF HIDECOLLINESIFEMPTY}
  If HIDECOLLINESIFEMPTY And colline And Not (dgColLines In Self.Options) Then
    Self.Options := Self.Options + [dgColLines]
  Else If HIDECOLLINESIFEMPTY And Not colline And (dgColLines In Self.Options) Then
    Self.Options := Self.Options - [dgColLines];
  {$ENDIF}

  inherited;

  Self.DefaultDrawColumnCell(Rect, DataCol, Column, State);
End;

Supports VCL styles and works fine for a couple of years now.

 

I used the "lazy" technique. Save this as uDBGrid.pas, add is to the uses clause of your form and in the declaration change "grdMyDBGrid: TDBGrid;" to "grdMyDBGrid: uDBGrid.TDBGrid". Since the class name is the same no modification in the dfm is necessary.

Drawback is, it only works runtime. And it's hacky.

Edited by aehimself
  • Like 1

Share this post


Link to post
2 hours ago, aehimself said:

// This code imitates the highlight of the whole row even if RowSelect is disabled. Note that it needs MultiSelect to be enabled!
//  If Not (gdSelected In State) And grid.SelectedRows.CurrentRowSelected Then
//    grid.Canvas.Brush.Color := clHighLight

Woah, you saved the day!!!
I tried it and it works! That trick actually doesn't require to make a new class, install components, do hacky things!. It is enough in your app to write a handler for OnDrawColumnCell !!!

Event the handler is almost the same:
 

  if not (gdSelected in State) and Grid.SelectedRows.CurrentRowSelected then
    Grid.Canvas.Brush.Color := TColor($FFFFA0)

 

Edited by Squall_FF8

Share this post


Link to post
3 hours ago, Softacom | Company said:

Yes, it’s not so hard to replace component. You need to follow next steps:

1.       Create package with your new component and install it into Delphi (don’t forget to make package DesingnTime and make register procedure). We need it to make designer works.

2.       Replace text ': TDBGrid'#13#10 with ': TMyGrid'#13#10 in *.dfm files

3.       Replace text ': TDBGrid;'#13#10 with ': TMyGrid;'#13#10 in *.pas files

4.       We need to add your pas-file with new type into interface uses section to let compiler find you new class. You can do it in a few ways, but I suggest, replace text ‘Vcl.DBGrids’ with ‘Vcl.DBGrids, MyNewGrid’ in *.pas files

5.       If your project not single exe-file and use own bpl-files with this new grid, than you need to add new package into requires section.

6.       If your project cannot compile after all this steps – maybe you will need to add path for this new pas-file into “search path” section in options of your project

Thank you very much for the solid advice! 
For completeness and people with the same problem, the solution is is simple:
 

type
  TDBHackGrid = class(TDBGrid)
  public
    property Col;
    property Row;
  end;

Then in your app you just compare: 

if Row = HackGrid.DataSource.RecNo then // code for coloring the row

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×