Delphi 10 TDrawGrid — как правильно обновить строки?

Использование Delphi 10.2 Токио.

Я использую метод DrawCell, чтобы все столбцы в строке имели тот же цвет, что и выбранная ячейка. Это позволяет мне разрешить пользователю щелкать по разным ячейкам, но при этом показывать «выбранную» строку.

При этом используется метод OnSelectCell для аннулирования исходной строки и вновь выбранной строки. Пользуюсь этим методом много лет.

Если у меня есть сетка с горизонтальной полосой прокрутки, сетка не отображается должным образом при прокрутке вправо, и пользователь щелкает ячейку.

Вот простой пример использования TDrawGrid с событиями OnDrawCell и OnSelectCell:

Код формы (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.

Нажмите на столбец 13 в строке 2.

Нажмите на столбец 12 в строке 3.

Заметили действительно запутанный шаблон выбора?

Вот скриншот результата:

снимок экрана с выделенным фрагментом

В идеале должен быть один ряд синих ячеек, а не беспорядочный беспорядок. Ряд 3 должен быть сплошным синим цветом.

Вызов DrawGrid1.Refresh в методе OnSelectCell даже не исправляет ситуацию.

Любые идеи о том, как заставить это действительно работать? Я не могу использовать RowSelect для этой сетки.

Ваше здоровье!

TJ


person TJ Asher    schedule 16.07.2018    source источник
comment
см. qc.embarcadero.com/wc/qcmain.aspx?d=81060 это ошибка, существующая с самого начала delphi   -  person Triber    schedule 17.07.2018


Ответы (2)


Если не считать ненужного мерцания, в вашем коде, похоже, нет ошибок. Это можно исправить с помощью события 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 из TCustomGrid, она не учитывает возможную прокрутку. То же самое для столбцов.

Вы можете использовать защищенный метод BoxRect, который использует метод GridRectToScreenRect (закрытый) для преобразования позиций ячеек в координаты экрана.

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;
person Sertac Akyuz    schedule 16.07.2018

Это связано с ошибкой в ​​подпрограммах 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);    

or

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