Как заполнить ячейку строковой сетки, используя собственный цвет?

Я пытаюсь написать собственное средство выбора даты (календарь). Даты будут отображаться на сетке строк. Я пытаюсь заполнить выбранную ячейку своим цветом и выделить выбранный текст ячейки жирным шрифтом.

Вот мой код:

    type
      TStringGrid = Class(Vcl.Grids.TStringGrid)
      private
        FHideFocusRect: Boolean;
      protected
         Procedure Paint;override;
      public
         Property HideFocusRect:Boolean Read FHideFocusRect Write FHideFocusRect;
      End;


    TfrmNepaliCalendar = class(TForm)
    ...
    ...
    ...
    end;


    procedure TfrmNepaliCalendar.StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    begin
       if gdSelected in State then begin
        StringGrid.Canvas.Brush.Color := $00940A4B;
        StringGrid.Canvas.FillRect(Rect);

        StringGrid.Canvas.Font.Style := [fsBold];
        StringGrid.Canvas.Font.Color := clHighlightText;
        StringGrid.Canvas.TextOut(Rect.Left + 3, Rect.Top + 5, StringGrid.Cells[ACol,ARow]);

        StringGrid.HideFocusRect := True;
      end;
    end;


{ TStringGrid }

procedure TStringGrid.Paint;
var
  LRect: TRect;
begin
  inherited;
  if HideFocusRect then begin
    LRect := CellRect(Col,Row);
    if DrawingStyle = gdsThemed then InflateRect(LRect,-1,-1);

    DrawFocusrect(Canvas.Handle,LRect)
  end;
end;

Вывод, я получаю:

При нажатии на ячейку без текста

При нажатии фон обрезается слева

Проблема № 1: мне нужно скрыть этот нежелательный прямоугольник, появляющийся в качестве границы для выбранной ячейки

Проблема № 2: избегайте обрезки фона ячейки


person Rabi Jayasawal    schedule 14.10.2015    source источник


Ответы (2)


В процедуре OnDrawCell добавьте непосредственно перед FillRect

Rect.Left := Rect.Left-4;

Кажется, работает.


Альтернатива

Вышеупомянутое не полностью решает проблему фокусировки даже с вашим дополнением к процедуре рисования. Иногда белая линия видна только внутри границ ячейки.

Но ниже приведена альтернатива, которая решает обе ваши проблемы. Это требует немного больше кодирования, но не так много. С другой стороны, создание подкласса TStringGrid не требуется, ни настройка Rect

Основой является отключение рисования по умолчанию, поэтому установите свойство сетки DefaultDrawing := false;, а затем добавьте к событию OnDrawCell:

procedure TForm1.StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  if gdFixed in State then
  begin
    StringGrid.Canvas.Brush.Color := clGradientInactiveCaption;
    StringGrid.Canvas.Font.Style := [];
    StringGrid.Canvas.Font.Color := clBlack;
  end
  else
  if gdSelected in State then
  begin
    StringGrid.Canvas.Brush.Color := $00940A4B;
    StringGrid.Canvas.Font.Style := [fsBold];
    StringGrid.Canvas.Font.Color := clHighlightText;
  end
  else
  begin
    StringGrid.Canvas.Brush.Color := $00FFFFFF;
    StringGrid.Canvas.Font.Style := [];
    StringGrid.Canvas.Font.Color := clWindowText;
  end;

  StringGrid.Canvas.FillRect(Rect);
  StringGrid.Canvas.TextOut(Rect.Left + 3, Rect.Top + 5, StringGrid.Cells[ACol,ARow]);
end;

Если отрисовка по умолчанию отключена, сетка рисует рамку сетки и линии сетки, но оставляет программисту все остальное рисование. Предостережение в том, что вы должны сами добавить причудливый тематический рисунок, если вам это нужно. С приведенным выше кодированием я получаю такой результат:

Образец сетки

person Tom Brunberg    schedule 14.10.2015
comment
Я удалил свой ответ по вашему предложению. Однако, я полагаю, вы можете использовать предложение из первой его части в своем ответе. StringGrid.Canvas.FillRect(TStringGrid(Sender).CellRect(ACol, ARow)); кажется более естественным, чем Rect.Left := Rect.Left-4;. Это может помочь ОП. - person asd-tm; 14.10.2015
comment
@ asd-tm О, я не предлагал удалить ваш ответ. Без рисования по умолчанию -4 больше не нужен! Вызов свойства function CellRect(), just to change the Left` кажется излишним в методе Paint. Если вы посмотрите в исходный код, вы увидите, что ARect.Left увеличивается на очень буквальную «4». Номер строки не могу сказать, она у меня больше не открыта. - person Tom Brunberg; 14.10.2015
comment
О нет, Том, я не виню тебя. Более того, я благодарю вас за ваше предложение. Теперь я понял вашу точку зрения о буквальном 4 и поддержал ваш ответ. - person asd-tm; 14.10.2015
comment
Идеально. Второе решение работает нормально, как и ожидалось. Большое спасибо. - person Rabi Jayasawal; 15.10.2015

Я предполагаю, что вы (хотите) использовать настройку по умолчанию DefaultDrawing = True, иначе вашего вопроса не существует.

  1. Чтобы избавиться от прямоугольника фокуса, вам нужно отрисовать его снова (поскольку это XOR-операция, прямоугольник фокуса исчезнет) или предотвратить его отрисовку.

    Рисование снова выполняется с использованием события OnDrawCell:

    procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    begin
      if gdFocused in State then
        DrawFocusRect(StringGrid1.Canvas.Handle, Rect);
    end;
    

    Предотвращение рисования вообще, например. делается путем отключения возможности установить фокус на StringGrid. Я предполагаю, что вы не используете его редактор, так что это не должно вызывать дополнительных проблем с удобством использования.

    type
      TStringGrid = class(Vcl.Grids.TStringGrid)
      public
        function CanFocus: Boolean; override;
      end;
    
    function TStringGrid.CanFocus: Boolean;
    begin
      Result := False;
    end;
    

    На самом деле это немного странное рабочее решение, потому что вы все еще можете входить в элемент управления, и он продолжает реагировать на события клавиатуры.

  2. Я не могу воспроизвести вашу проблему с отсечением с помощью этого кода (здесь XE2):

    procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    begin
      if gdSelected in State then
      begin
        StringGrid1.Canvas.Brush.Color := $00940A4B;
        StringGrid1.Canvas.FillRect(Rect);
        StringGrid1.Canvas.Font.Style := [fsBold];
        StringGrid1.Canvas.Font.Color := clHighlightText;
        StringGrid1.Canvas.TextOut(Rect.Left + 3, Rect.Top + 5,
          StringGrid1.Cells[ACol, ARow]);
      end;
    end;
    

    Rect будет правильным CellRect. Эффект отсечения возникает из-за чего-то еще в другом месте.

    Но если в исходном коде XE8 действительно есть ложное +4, например Том Брунберг упоминает, что легко преодолевается с помощью -4, тогда это, очевидно, является ошибкой и о ней следует сообщить.

person NGLN    schedule 14.10.2015
comment
Что касается отсечения, я использую XE7, но вижу точно такое же отсечение, что и Раби. В Vcl.Grids TStringGrid.DrawCell(), if DefaultDrawing then begin if StyleServices.Enabled then begin ARect.Left := ARect.Left + 4;. Затем ARect передается inherited DrawCell(), который вызывает FOnDrawCell(). Я не устанавливал XE8. Не могли бы вы подтвердить, изменяет ли XE8 ARect.Left таким же образом? - person Tom Brunberg; 14.10.2015
comment
Что касается прямоугольника фокуса, +1 за DrawFocusRect() в OnDrawCell и за упоминание о том, что это функция XOR, я этого не знал. Это работает, но Rect (исправлено -4) должен быть увеличен на -1, -1, чтобы соответствовать внутреннему рисунку сетки, и он должен быть в конце OnDrawCell, если Rect заполняется раньше. - person Tom Brunberg; 14.10.2015
comment
@Tom Ага, StyleServices.Enabled, теперь понятно! Действительно, в XE2 такая же ошибка. - person NGLN; 14.10.2015
comment
@NGLN Используя метод № 1, я мог получить требуемый результат, за исключением того, что мне не удалось заполнить собственный цвет. Кстати, при использовании метода № 2 возникает та же проблема с отсечением. - person Rabi Jayasawal; 15.10.2015