Delphi 10 TDrawGrid-如何使行正确刷新?

时间:2018-07-16 19:12:37

标签: delphi delphi-10.2-tokyo

使用Delphi 10.2 Tokyo。

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

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

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

这是一个简单的示例,该示例使用带有OnDrawCell事件和OnSelectCell事件的TDrawGrid:

表格(DFM)代码:

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)代码:

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 capture of borked selection

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

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

关于如何使其真正起作用的任何想法?我不能为此网格使用RowSelect。

干杯!

TJ

2 个答案:

答案 0 :(得分:5)

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

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
    ...



该错误位于InvalidateRow的{​​{1}}中,它不能说明可能的滚动。列方式相同。

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

GridRectToScreenRect

答案 1 :(得分:0)

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

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;

解决此问题的方法:

   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);    

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