关于 Delphi 10 TDrawGrid:Delphi 10 TDrawGrid – 如何正确刷新行?

Delphi 10 TDrawGrid - How do I get rows to refresh properly?

使用 Delphi 10.2 Tokyo。

我使用 DrawCell 方法使一行中的所有列与所选单元格的颜色相同。这允许我让用户单击不同的单元格,但仍显示"选定"行。

这使用 OnSelectCell 方法使原始行和新选择的行无效。多年来一直使用这种方法。

如果我有一个带有水平滚动条的网格,则当向右滚动并且用户单击单元格时,该网格不会正确绘制。

下面是一个使用 TDrawGrid 和 OnDrawCell 事件和 OnSelectCell 事件的简单示例:

表格 (DFM) 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object DrawGrid1: TDrawGrid
    Left = 0
    Top = 0
    Width = 635
    Height = 299
    Align = alClient
    Color = clWhite
    ColCount = 15
    DefaultColWidth = 65
    DefaultRowHeight = 48
    DefaultDrawing = False
    DrawingStyle = gdsGradient
    RowCount = 12
    GradientEndColor = clBtnFace
    GradientStartColor = clBtnFace
    Options = [goThumbTracking]
    ParentShowHint = False
    ShowHint = True
    TabOrder = 0
    OnDrawCell = DrawGrid1DrawCell
    OnSelectCell = DrawGrid1SelectCell
    ColWidths = (
      65
      65
      65
      65
      65
      65
      65
      65
      65
      65
      65
      65
      65
      65
      65)
    RowHeights = (
      48
      48
      48
      48
      48
      48
      48
      48
      48
      48
      48
      48)
  end
end

单位 (PAS) 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,     System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Math;

type
  TGridCracker = class(TDrawGrid)// required to access protected method Invalidaterow - info gleaned from Team B member Peter Below on the Internet
  private
  public
  end;

  TForm1 = class(TForm)
    DrawGrid1: TDrawGrid;
    procedure DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
    procedure DrawGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
var MyCanvas : TCanvas;
  str : string;
  MyRect : TRect;
begin
  MyCanvas := TDrawGrid(Sender).Canvas;

  MyCanvas.Font.Name := 'Arial'; // drawgrid uses Tahoma 8pt as its default font, not Arial
  MyCanvas.Font.Size := 9;
  MyCanvas.Brush.Color := TDrawGrid(Sender).FixedColor;
  MyCanvas.Font.Color := TDrawGrid(Sender).Font.Color;
  MyCanvas.FillRect(Rect);

  if (ARow = 0) then begin
    str := EmptyStr;
    if (ACol > 0) then begin
        str := ACol.ToString;
    end
    else begin
      str := 'TEST';
    end;

    MyCanvas.Font.Color := clblack; // clGray;
    MyRect.Left := Rect.Left + 1;
    MyRect.Top := Rect.Top + 3;
    MyRect.Right := Rect.Right - 1;
    MyRect.Bottom := Rect.Bottom - 3;
    MyCanvas.FillRect(MyRect);
    MyCanvas.Brush.Color := clGray;
    MyCanvas.FrameRect(MyRect);
    MyCanvas.Brush.Color := clWhite;
    MyCanvas.Font.Style := MyCanvas.Font.Style + [fsBold];

    MyRect.Top := MyRect.Top + 2;
    DrawText(MyCanvas.Handle, pChar(str), -1, MyRect, DT_VCENTER or DT_CENTER);

    MyCanvas.Font.Style := MyCanvas.Font.Style - [fsBold];
  end
  else begin
    if (ACol = 0) then begin
      MyCanvas.Brush.Color := clMaroon;
      MyCanvas.FillRect(Rect);
    end
    else begin//ACol > 0
      if ARow = DrawGrid1.Row then begin
        MyCanvas.Brush.Color := clBlue;
      end
      else begin
        MyCanvas.Brush.Color := clwhite;
      end;

      MyCanvas.FillRect(Rect);

      // other cell drawing of text happens after here
    end;
  end;
end;

procedure TForm1.DrawGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
begin
  TGridCracker(Sender).InvalidateRow(TGridCracker(Sender).Row);
  TGridCracker(Sender).InvalidateRow(ARow);
end;

end.

运行程序。

单击水平滚动条,使第 14 列可见。

单击第 2 行中的第 13 列。

单击第 3 行中的第 12 列。

注意到真正混乱的选择模式了吗?

这是结果的屏幕截图:

screen

理想情况下,应该有一排蓝色单元格,而不是乱七八糟的。第 3 行应该是纯蓝色。

在 OnSelectCell 方法中调用 DrawGrid1.Refresh 甚至无法修复它。

关于如何使它真正起作用的任何想法?我不能对这个网格使用 RowSelect。

干杯!

TJ


除了不必要的闪烁之外,您的代码似乎没有任何错误。这可以通过使用 OnDrawCell 事件的 State 来解决。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure TForm1.DrawGrid1DrawCell(Sender: TObject; ....
var MyCanvas : TCanvas;
  str : string;
  MyRect : TRect;
begin
  MyCanvas := TDrawGrid(Sender).Canvas;

  if gdFixed in State then begin
    MyCanvas.Font.Name := 'Arial'; // drawgrid uses Tahoma 8pt as its default font, not Arial
    MyCanvas.Font.Size := 9;
    MyCanvas.Brush.Color := TDrawGrid(Sender).FixedColor;
    MyCanvas.Font.Color := TDrawGrid(Sender).Font.Color;
    MyCanvas.FillRect(Rect);
  end;

  if (ARow = 0) then begin
    ...

错误在 TCustomGridInvalidateRow 中,它不考虑可能的滚动。列方式相同。

您可以使用受保护的 BoxRect 方法,该方法使用 GridRectToScreenRect(私有)方法将单元格位置转换为屏幕坐标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
procedure TForm1.DrawGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
var
  Grid: TDrawGrid;
  GR, R: TRect;
begin
  Grid := Sender as TDrawGrid;
  if ARow = Grid.Row then
    Exit;

  GR.Left := Grid.LeftCol;
  GR.Top := Grid.Row;
  GR.Width := Grid.VisibleColCount;
  GR.Height := 0;

  R := TGridCracker(Grid).BoxRect(GR.Left, GR.Top, GR.Right, GR.Bottom);
  InvalidateRect(Grid.Handle, R, False);

  GR.Top := ARow;
  GR.Bottom := ARow;

  R := TGridCracker(Grid).BoxRect(GR.Left, GR.Top, GR.Right, GR.Bottom);
  InvalidateRect(Grid.Handle, R, False);
end;


这是由于 VCL TCustomGrid.InvalidateRow(和 TCustomGrid.InvalidateCol)例程中的一个错误:

1
2
3
4
5
6
7
8
9
10
11
procedure TCustomGrid.InvalidateRow(ARow: Longint);
var
  Rect: TGridRect;
begin
  if not HandleAllocated then Exit;
  Rect.Top := ARow;
  Rect.Left := 0; // this should be Rect.Left:=LeftCol; --> index of the first column in the scrollable region that is visible
  Rect.Bottom := ARow;
  Rect.Right := VisibleColCount+1;
  InvalidateRect(Rect);
end;

解决此问题的方法:

1
2
3
4
5
6
7
8
9
10
11
12
   type TGridCracker = class(TDrawGrid)
   protected
    procedure InvalidateRow(ARow: Longint);
   end;

   procedure TGridCracker.InvalidateRow(ARow: Integer);
   var i: Integer;
   begin
     if not HandleAllocated then
        Exit;
     for i := 0 to ColCount-1 do // this will invalidate all cells, visible and hidden
         InvalidateCell(i, ARow);

1
2
3
     for i := LeftCol to LeftCol+VisibleColCount do // this will invalidate only visible cells
         InvalidateCell(i, ARow);    
   end;