How To Toggle The cell Colour And Text In A TStringGrid On And Off

橙三吉。 提交于 2019-12-23 18:26:01

问题


I am running Lazarus v0.9.30 (32 bit compiler).

I have a TForm with a standard TStringGrid on it. The grid has the following properties set. RowCount = 5, ColumnCount = 5, FixedCols = 0, FixedRows = 0.

I Googled some code that showed me how to change the cell colour and add some text to the cell when a user clicks on a TStringGrid cell. All works fine and I have extended it slightly to toggle the color/text on and off on the GridClick event.

The questions I have are more to better understand the purpose behind some of the elements of the code.

There is an array of Foregroud (FG) and Background (BG) TColor objects. Are they there to store the cell color attributes that are set on the GridClick event, so if the DrawCell event needs to get triggered again for any reason the cell can redraw itself? Can you avoid using the array of TColors and just set the colour / text in the DrawCell event as required?

If you need to use the arrays, I would assume that dimensions must match the Grid.ColCount and Grid.RowCount (ie. set via the SetLength call in Form.Create)

Is there a way to detect that you are clicking outside of the 5 x 5 cells of the stringgrid (ie in the whitespace) and thus prevent the GridClick from calling the DrawCell event. No matter where you click you always get a valid value for Row and Col.

unit testunit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  ExtCtrls, Menus, ComCtrls, Buttons, Grids, StdCtrls, Windows, Variants,
  LCLType;
type

  { TForm1 }

  TForm1 = class(TForm)
    Grid: TStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure GridClick(Sender: TObject);
    procedure GridDrawCell(Sender: TObject; aCol, aRow: Integer;
      aRect: TRect; aState: TGridDrawState);
  end; 

var
  Form1: TForm1; 

implementation

var
  FG: array of array of TColor;
  BG: array of array of TColor;

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
var
  Col, Row: integer;
begin
  // Set the sizes of the arrays
  SetLength(FG, 5, 5);
  SetLength(BG, 5, 5);

  // Initialize with default colors
  for Col := 0 to Grid.ColCount - 1 do begin
    for Row := 0 to Grid.RowCount - 1 do begin
      FG[Col, Row] := clBlack;
      BG[Col, Row] := clWhite;
    end;
  end;
end;

procedure TForm1.GridDrawCell(Sender: TObject; aCol, aRow: Integer;
  aRect: TRect; aState: TGridDrawState);
var
  S: string;
begin
  S := Grid.Cells[ACol, ARow];

  // Fill rectangle with colour
  Grid.Canvas.Brush.Color := BG[ACol, ARow];
  Grid.Canvas.FillRect(aRect);

  // Next, draw the text in the rectangle
  Grid.Canvas.Font.Color := FG[ACol, ARow];
  Grid.Canvas.TextOut(aRect.Left + 22, aRect.Top + 2, S);
end;

procedure TForm1.GridClick(Sender: TObject);
var
  Col, Row: integer;
begin
  Col := Grid.Col;
  Row := Grid.Row;

  // Set the cell color and text to be displayed
  if (Grid.Cells[Col,Row] <> 'Yes') then
    begin
      BG[Col, Row] := rgb(131, 245, 44);
      FG[Col, Row] := RGB(0, 0, 0);
      Grid.Cells[Col, Row] := 'Yes'
    end {if}
  else
    begin
      BG[Col, Row] := rgb(255, 255, 255);
      FG[Col, Row] := RGB(255, 255, 255);
      Grid.Cells[Col, Row] := '';
    end; {else}
end;

end.

回答1:


If you set the AllowOutboundEvents to False, the OnClick event will be fired only when you click on a certain cell, not when you click on a whitespace. So if you use this property, you will always get valid cell coordinates when you click somewhere.

procedure TForm1.FormCreate(Sender: TObject);
begin
  StringGrid1.AllowOutboundEvents := False;
  ...
end; 

Another point is that you should use the OnPrepareCanvas event instead of OnDrawCell, because in OnDrawCell you would have to paint everything, including text rendering. With OnPrepareCanvas you just set the Brush.Color and Font.Color for each cell which is going to be rendered.

And, you don't need to use arrays, you can use the Objects like you did with your columns, but surely you can keep the colors in the arrays. In the following example I've used the Objects and there's also shown the usage of the OnPrepareCanvas event, but note that this example as well as yours from the question colorize all cells including the fixed ones:

type
  TCellData = class(TObject)
  private
    FStateYes: Boolean;
    FForeground: TColor;
    FBackground: TColor;
  public
    property StateYes: Boolean read FStateYes write FStateYes;
    property Foreground: TColor read FForeground write FForeground;
    property Background: TColor read FBackground write FBackground;
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Col, Row: Integer;
  CellData: TCellData;
begin
  for Col := 0 to StringGrid1.ColCount - 1 do
    for Row := 0 to StringGrid1.RowCount - 1 do
    begin
      CellData := TCellData.Create;
      CellData.StateYes := False;
      CellData.Foreground := clBlack;
      CellData.Background := clWhite;
      StringGrid1.Objects[Col, Row] := CellData;
    end;
  StringGrid1.AllowOutboundEvents := False;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  Col, Row: Integer;
begin
  for Col := 0 to StringGrid1.ColCount - 1 do
    for Row := 0 to StringGrid1.RowCount - 1 do
      StringGrid1.Objects[Col, Row].Free;
end;

procedure TForm1.StringGrid1Click(Sender: TObject);
var
  Col, Row: Integer;
  CellData: TCellData;
begin
  Col := StringGrid1.Col;
  Row := StringGrid1.Row;

  if StringGrid1.Objects[Col, Row] is TCellData then
  begin
    CellData := TCellData(StringGrid1.Objects[Col, Row]);
    if CellData.StateYes then
    begin
      StringGrid1.Cells[Col, Row] := '';
      CellData.StateYes := False;
      CellData.Foreground := RGB(255, 255, 255);
      CellData.Background := RGB(255, 255, 255);
    end
    else
    begin
      StringGrid1.Cells[Col, Row] := 'Yes';
      CellData.StateYes := True;
      CellData.Foreground := RGB(0, 0, 0);
      CellData.Background := RGB(131, 245, 44);
    end;
  end;
end;

procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
  aState: TGridDrawState);
var
  CellData: TCellData;
begin
  if StringGrid1.Objects[ACol, ARow] is TCellData then
  begin
    CellData := TCellData(StringGrid1.Objects[ACol, ARow]);
    StringGrid1.Canvas.Brush.Color := CellData.Background;
    StringGrid1.Canvas.Font.Color := CellData.Foreground;
  end;
end;


来源:https://stackoverflow.com/questions/9684266/how-to-toggle-the-cell-colour-and-text-in-a-tstringgrid-on-and-off

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!