* * *

Author Topic: Highlighting a StringGrid Cell?  (Read 586 times)

segfault

  • Jr. Member
  • **
  • Posts: 60
Highlighting a StringGrid Cell?
« on: October 15, 2018, 10:36:57 am »
There are some examples of how to do this on the net, mainly using Delphi, but I can't seem to get it to work for my particular application. What I want to do is change the background/text colour of a cell when the button is clicked (background colour to yellow, text to black). See screenshot below.

https://i.postimg.cc/9M7df9Mg/grid.png

Here is my code so far :

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids,
  9.   StdCtrls, Types;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     StringGrid1: TStringGrid;
  18.     procedure FormCreate(Sender: TObject);
  19.     procedure StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  20.       aRect: TRect; aState: TGridDrawState);
  21.   private
  22.  
  23.   public
  24.  
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31. const
  32.   N : array[0..10,0..10] of byte =
  33.   ((0,1,2,3,4,4,3,2,1,1,0),
  34.    (1,0,1,3,4,4,4,3,2,1,0),
  35.    (2,1,0,2,3,4,5,4,3,2,0),
  36.    (3,3,2,0,2,4,5,6,5,3,1),
  37.    (4,4,3,2,0,2,5,7,7,5,2),
  38.    (4,4,4,4,2,0,3,7,9,8,4),
  39.    (3,4,5,5,5,3,0,5,9,11,8),
  40.    (2,3,4,6,7,7,5,0,8,15,15),
  41.    (1,2,3,5,7,9,9,8,0,15,30),
  42.    (1,1,2,3,5,8,11,15,15,0,61),
  43.    (0,0,0,1,2,4,8,15,30,61,0));
  44.  
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. procedure TForm1.FormCreate(Sender: TObject);
  50. var
  51.   i, j : byte;
  52. begin
  53.   // load numbers into grid
  54.   for i := 0 to 10 do
  55.     for j := 0 to 10 do
  56.       StringGrid1.Cells[i,j] := IntToStr(N[i,j]);
  57. end;
  58.  
  59. procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  60.   aRect: TRect; aState: TGridDrawState);
  61. var
  62.   r, c : byte;
  63. begin
  64.   // set background/font colours
  65.   for r := 0 to 10 do
  66.     for c := r + 1 to 10 do
  67.       if (aCol = c) and (aRow = r) then
  68.         with TStringGrid(sender) do begin
  69.           Canvas.Brush.Color:= clRed;
  70.           canvas.Font.Color:= clWhite;
  71.           canvas.FillRect(aRect);
  72.           Canvas.TextOut(aRect.Left+2,aRect.Top+2,Cells[aCol, aRow]);
  73.         end;
  74.   for c := 0 to 10 do
  75.     for r := c + 1 to 10 do
  76.       if (aCol = c) and (aRow = r) then
  77.         with TStringGrid(sender) do begin
  78.           Canvas.Brush.Color:= clBlack;
  79.           canvas.Font.Color:= clWhite;
  80.           canvas.FillRect(aRect);
  81.           Canvas.TextOut(aRect.Left+2,aRect.Top+2,Cells[aCol, aRow]);
  82.         end;
  83. end;
  84.  
  85. end.
                   
There seem to be several ways to do what I want but I don't understand very well how any of them work. For example, what is that fires the DrawCell procedure? and since the basic colours don't change apart from when a cell is highlighted, shouldn't initializing the colours  be in the FormCreate procedure? but I'm not sure how to do that.

I've had a look at the StringGrid reference on the Wiki but it's just confusing me. To be honest I think I'm having these problems because I've never really understand the basic object model which underlies how the GUI works, and I've never found a good explanation of it.
Anyway, thanks in advance for any help.

segfault

  • Jr. Member
  • **
  • Posts: 60
Re: Highlighting a StringGrid Cell?
« Reply #1 on: October 15, 2018, 11:22:27 am »
Adding an onSelectCell event works but only for the cells which haven't been painted (the middle diagonal).

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids,
  9.   StdCtrls, Types;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     StringGrid1: TStringGrid;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  21.       aRect: TRect; aState: TGridDrawState);
  22.     procedure StringGrid1SelectCell(Sender: TObject; aCol, aRow: Integer;
  23.       var CanSelect: Boolean);
  24.   private
  25.  
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34. const
  35.   N : array[0..10,0..10] of byte =
  36.   ((0,1,2,3,4,4,3,2,1,1,0),
  37.    (1,0,1,3,4,4,4,3,2,1,0),
  38.    (2,1,0,2,3,4,5,4,3,2,0),
  39.    (3,3,2,0,2,4,5,6,5,3,1),
  40.    (4,4,3,2,0,2,5,7,7,5,2),
  41.    (4,4,4,4,2,0,3,7,9,8,4),
  42.    (3,4,5,5,5,3,0,5,9,11,8),
  43.    (2,3,4,6,7,7,5,0,8,15,15),
  44.    (1,2,3,5,7,9,9,8,0,15,30),
  45.    (1,1,2,3,5,8,11,15,15,0,61),
  46.    (0,0,0,1,2,4,8,15,30,61,0));
  47.  
  48. {$R *.lfm}
  49.  
  50. { TForm1 }
  51.  
  52. procedure TForm1.FormCreate(Sender: TObject);
  53. var
  54.   i, j : byte;
  55. begin
  56.   // load numbers into grid
  57.   for i := 0 to 10 do
  58.     for j := 0 to 10 do
  59.       StringGrid1.Cells[i,j] := IntToStr(N[i,j]);
  60. end;
  61.  
  62. procedure TForm1.Button1Click(Sender: TObject);
  63. begin
  64.   StringGrid1.Row:=4;
  65.   StringGrid1.Col:=4;
  66.   StringGrid1.SetFocus;
  67. end;
  68.  
  69. procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  70.   aRect: TRect; aState: TGridDrawState);
  71. var
  72.   r, c : byte;
  73. begin
  74.   // set background/font colours
  75.   for r := 0 to 10 do
  76.     for c := r + 1 to 10 do
  77.       if (aCol = c) and (aRow = r) then
  78.         with TStringGrid(sender) do begin
  79.           Canvas.Brush.Color:= clRed;
  80.           canvas.Font.Color:= clWhite;
  81.           canvas.FillRect(aRect);
  82.           Canvas.TextOut(aRect.Left+2,aRect.Top+2,Cells[aCol, aRow]);
  83.         end;
  84.   for c := 0 to 10 do
  85.     for r := c + 1 to 10 do
  86.       if (aCol = c) and (aRow = r) then
  87.         with TStringGrid(sender) do begin
  88.           Canvas.Brush.Color:= clBlack;
  89.           canvas.Font.Color:= clWhite;
  90.           canvas.FillRect(aRect);
  91.           Canvas.TextOut(aRect.Left+2,aRect.Top+2,Cells[aCol, aRow]);
  92.         end;
  93. end;
  94.  
  95. procedure TForm1.StringGrid1SelectCell(Sender: TObject; aCol, aRow: Integer;
  96.   var CanSelect: Boolean);
  97. begin
  98.   StringGrid1.SelectedColor:= clBlue;
  99.   StringGrid1.Options := StringGrid1.Options + [goDrawFocusSelected];
  100. end;
  101.  
  102. end.
  103.                            

paweld

  • Full Member
  • ***
  • Posts: 171
Re: Highlighting a StringGrid Cell?
« Reply #2 on: October 15, 2018, 11:34:03 am »
Code: Pascal  [Select]
  1. procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  2.   aRect: TRect; aState: TGridDrawState);
  3. var
  4.   r, c : byte;
  5. begin
  6.   // set background/font colours
  7.   for r := 0 to 10 do
  8.     for c := r + 1 to 10 do
  9.       if (aCol = c) and (aRow = r) then
  10.         with TStringGrid(sender) do begin
  11.           Canvas.Brush.Color:= clRed;
  12.           canvas.Font.Color:= clWhite;
  13.           canvas.FillRect(aRect);
  14.           Canvas.TextOut(aRect.Left+2,aRect.Top+2,Cells[aCol, aRow]);
  15.         end;
  16.   for c := 0 to 10 do
  17.     for r := c + 1 to 10 do
  18.       if (aCol = c) and (aRow = r) then
  19.         with TStringGrid(sender) do begin
  20.           Canvas.Brush.Color:= clBlack;
  21.           canvas.Font.Color:= clWhite;
  22.           canvas.FillRect(aRect);
  23.           Canvas.TextOut(aRect.Left+2,aRect.Top+2,Cells[aCol, aRow]);
  24.         end;
  25.   //set select
  26.   if (gdSelected in aState) or (gdFocused in aState) then
  27.   begin
  28.     StringGrid1.Canvas.Brush.Color:=clBlue;
  29.     StringGrid1.Canvas.Font.Style:=StringGrid1.Canvas.Font.Style+[fsBold];
  30.     StringGrid1.Canvas.FillRect(aRect);
  31.     StringGrid1.Canvas.TextOut(aRect.Left+2, aRect.Top+2, StringGrid1.Cells[aCol, aRow]);
  32.   end;
  33. end;    
Best regards
paweld

segfault

  • Jr. Member
  • **
  • Posts: 60
Re: Highlighting a StringGrid Cell?
« Reply #3 on: October 15, 2018, 12:34:35 pm »
Many thanks paweld.  :)

It works now, and more importantly, I understand why. Easy when you know how.  ::)

wp

  • Hero Member
  • *****
  • Posts: 5034
Re: Highlighting a StringGrid Cell?
« Reply #4 on: October 15, 2018, 06:01:37 pm »
I would not use the OnDrawCell event for this purpose. What if the column and row headers of the grid are themed (i.e. Grid.TitleStyle = tsNative), or if there are sorting icons in the column headers? You fully paint the headers - theming will be lost, sorting icons will be gone. What if your grid supports Columns and one column has ButtonStyle = cbsCheckboxColumn, i.e. should display a checkbox in each cell of that column? This code does not paint any checkboxes... What if the cells of the 2nd column will be centered? You can achieve this by setting the Alignment of the corresponding Column accordinging. But your code ignores it - you have to redo all the formattings which have been prepared.

The Lazarus way to modifiy the canvas attributes used by the painting process is to hook into the OnPrepareCanvas event. Before Lazarus draws a cell it sets up all canvas properties to the required values in the PrepareCanvas method; at the end of this method it fires the event OnPrepareCanvas which gives the opportunity to modify these properties again. Then the cell is drawn (with all its gimmics: themed header columns, sorting icons, checkboxes, left/center/right alignment etc) using the canvas settings prepared immediately before. So, you only must write an event handler for OnPrepareCanvas to set the color of the selected cell - this color will be used as cell background subsequently and everything of the cell format will be kept.
Code: Pascal  [Select]
  1. procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
  2.   aState: TGridDrawState);
  3. begin
  4.   if (aCol >= StringGrid1.FixedCols) and (aCol < aRow) then begin
  5.     StringGrid1.Canvas.Brush.Color := clBlack;
  6.     StringGrid1.Canvas.Font.Color := clWhite;
  7.   end else
  8.   if (aRow >= StringGrid1.FixedCols) and (aRow < aCol) then begin
  9.     StringGrid1.Canvas.Brush.Color := clRed;
  10.     StringGrid1.Canvas.Font.Color := clWhite;
  11.   end;
  12.   if (aCol = StringGrid1.Col) and (aRow = StringGrid1.Row) then begin
  13.     StringGrid1.Canvas.Brush.Color := clYellow;
  14.     StringGrid1.Canvas.Font.Color := clBlack;
  15.   end;
  16. end;
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Handoko

  • Hero Member
  • *****
  • Posts: 2631
  • My goal: build my own game engine using Lazarus
Re: Highlighting a StringGrid Cell?
« Reply #5 on: October 16, 2018, 04:45:29 am »
I recommend to use OnPrepareCanvas too. I forget what are the reasons but I still remember using OnDrawCell is not good for changing the color of cells.

If anyone interested to see the example of using OnPrepareCanvas, you can try this:
http://forum.lazarus.freepascal.org/index.php/topic,37181.msg249361.html#msg249361

GetMem

  • Hero Member
  • *****
  • Posts: 3202
Re: Highlighting a StringGrid Cell?
« Reply #6 on: October 16, 2018, 05:30:26 am »
Quote
I forget what are the reasons but I still remember using OnDrawCell is not good for changing the color of cells.
@wp already explained why OnPrepareCanvas is a much better approach, but you can also read if you wish the grid reference page: http://wiki.lazarus.freepascal.org/Grids_Reference_Page#Description_of_grid.27s_drawing_process

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus