2

I am trying to write custom date picker(calendar). The dates will be displayed on the stringgrid. I am trying to fill the clicked cell with a custom color and make that selected celltext bold.

Here is my code:

    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;

The output, I am getting:

When clicked on cell containing no text

When clicked, the background is clipped from left

Problem #1: I need to hide that unwanted rectangle appearing as border for the selected cell

Problem #2: Avoid the cell background clipping

Rabi Jayasawal
  • 441
  • 1
  • 9
  • 18

2 Answers2

5

In the OnDrawCell procedure add just before FillRect

Rect.Left := Rect.Left-4;

Seems to work.


An alternative

The above doesn't fully solve the focus issue even with your paint procedure addon. Sometimes a white line is visible just inside the cell borders.

But the following is an alternative, that solves both your issues. It requires a little more coding, but not so much. On the other hand, subclassing TStringGrid is not needed, neither the Rect adjustment

The basis is to disable default drawing, so set the grids property DefaultDrawing := false; and then add to the OnDrawCell event:

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;

With default drawing disabled, the grid draws the grid frame and the grid lines, but leaves all other drawing to the programmer. The caveat is that you have to add fancy themed drawing yourself if you need it. With above coding I get this result:

Sample grid

NGLN
  • 43,011
  • 8
  • 105
  • 200
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
  • 1
    I've removed my answer on your suggestion. However, I suppose, you can use a proposal from the firts part of it in your answer. `StringGrid.Canvas.FillRect(TStringGrid(Sender).CellRect(ACol, ARow));` seems more natural than `Rect.Left := Rect.Left-4;`. It might help the OP. – asd-tm Oct 14 '15 at 14:05
  • @asd-tm Oh, it was not my suggestion to remove your answer. Without default drawing the -4 is not needed anymore! A call to `function CellRect(), just to change the `Left` property seems like overkill within the `Paint` method. If you look in the source code you will see `ARect.Left` being increased with a very literal '4'. Can't say the line number, I don'y have it open anymore. – Tom Brunberg Oct 14 '15 at 14:16
  • Oh, no, Tom, I am not blaiming you. Moreover, I thank you for your suggestion. Now I got your point about `4` literal and upvote your answer. – asd-tm Oct 14 '15 at 14:51
  • Perfect. The second solution works fine as expected. Thanks alot. – Rabi Jayasawal Oct 15 '15 at 12:14
4

I assume you (want to) use the default DefaultDrawing = True setting, otherwise your question does not exist.

  1. To get rid of the focus rect, you need to draw it again (because it is a XOR-operation, the focus rect will disappear), or prevent it from being drawn.

    Drawing again is done by utilizing the OnDrawCell event:

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

    Preventing it from drawing at all e.g. is done by disabling the possibility to set focus to the StringGrid. I assume you do not use its editor, so that should give no further usability concerns.

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

    This actually is a bit strange working solution, because you are still able to tab into the control and it keeps responding to keyboard events.

  2. I cannot reproduce your cliping problem with this code (XE2 here):

    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;
    

    The Rect will be and ís the correct CellRect. The cliping effect is due to something else elsewhere.

    But if there really is a spurious +4 in the source code of XE8 like Tom Brunberg mentions, which is easily overcome with -4, then that obviously is a bug and should be reported.

Community
  • 1
  • 1
NGLN
  • 43,011
  • 8
  • 105
  • 200
  • Regarding clipping, I'm using XE7 but I see exactly the same clipping as Rabi. In Vcl.Grids, TStringGrid.DrawCell(), `if DefaultDrawing then begin if StyleServices.Enabled then begin ARect.Left := ARect.Left + 4;`. The ARect is then passed on to `inherited DrawCell()` which calls `FOnDrawCell()`. I haven't installed XE8, could you confirm if XE8 modifies `ARect.Left` in the same way? – Tom Brunberg Oct 14 '15 at 19:38
  • 2
    Regarding focus rect, +1 for `DrawFocusRect()` in `OnDrawCell` and for mentioning it's an XOR function, I did not know this. It works but the Rect (corrected -4) must be inflated -1, -1 to match the grids internal drawing, and it must be at the end of OnDrawCell in case the Rect is filled earlier. – Tom Brunberg Oct 14 '15 at 19:40
  • @Tom Aha, `StyleServices.Enabled`, now I see! Indeed, XE2 has the same bug. – NGLN Oct 14 '15 at 19:49
  • @NGLN Using method #1, I could get the required output except that I was unable to fill a custom color. Btw using method #2 there is still the same clipping problem. – Rabi Jayasawal Oct 15 '15 at 12:17