2

I know there's a way to highlight a cell in a TStringGrid. I could use that, but inputting the dates and days and months would be a big issue unless you know how to do that.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770

1 Answers1

5

Yes, this is easy if you only make a small modification to the control's source code. Specifically, we need to add a small amount of code to its DrawCell method.

Initially, this is

procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
  TheText: string;
begin
  TheText := CellText[ACol, ARow];
  with ARect, Canvas do
    TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
      Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
end;

Change this to:

procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
  TheText: string;
  i: Integer;
  Day: Integer;
begin
  TheText := CellText[ACol, ARow];
  with ARect, Canvas do
  begin
    Font.Style := [];
    for i := Low(HighlightDates) to High(HighlightDates) do
      if TryStrToInt(TheText, Day) then
        if SameDate(HighlightDates[i], EncodeDate(Year, Month, Day)) then
        begin
          Font.Style := [fsBold];
          Break;
        end;
    TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
      Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
  end;
end;

The easiest way to quickly try this is to use an interposer class:

type
  TCalendar = class(Vcl.Samples.Calendar.TCalendar)
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
  end;

  TForm1 = class(TForm)
    ...

Now you only need to supply an array of dates to highlight:

var
  HighlightDates: TArray<TDate>;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetLength(HighlightDates, 3);
  HighlightDates[0] := EncodeDate(2020, 07, 10);
  HighlightDates[1] := EncodeDate(2020, 07, 20);
  HighlightDates[2] := EncodeDate(2020, 08, 10);
end;

or, in new Delphi versions (XE7 and later),

procedure TForm1.FormCreate(Sender: TObject);
begin
  HighlightDates :=
    [
      EncodeDate(2020, 07, 10),
      EncodeDate(2020, 07, 20),
      EncodeDate(2020, 08, 10)
    ];
end;

Don't forget to add DateUtils to the uses clause.

Screenshot of a TCalendar control with two dates selected: 2020-07-10 and 2020-07-20.

(I apologise for the Swedish day names.)

Needless to say, you can paint highlighted cells in any way you like; making the font bold is only one possibility. If instead you want to highlight a cell by drawing a benzene ring in its top-right corner, that's fine too.

You will want to create a new control with the new code. In this, the date array would be a member. It could have an associated property with a setter that also invalidates the control. In addition, you could add public HighlightDate(const ADate: TDate) and StopHighlightDate(const ADate: TDate) procedures that add and remove dates from this array (and invalidate the control).

Update

On request (see comments), here is how to change the background colour of highlighted cells:

{ TCalendar }

procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect;
  AState: TGridDrawState);
var
  TheText: string;
  i: Integer;
  Day: Integer;
  OldColor: TColor;
begin
  TheText := CellText[ACol, ARow];
  with ARect, Canvas do
  begin
    OldColor := Brush.Color;
    for i := Low(HighlightDates) to High(HighlightDates) do
      if TryStrToInt(TheText, Day) then
        if SameDate(HighlightDates[i], EncodeDate(Year, Month, Day)) then
        begin
          Brush.Color := clSkyBlue;
          FillRect(ARect);
          Break;
        end;
    TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
      Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
    Brush.Color := OldColor;
  end;
end;

Screenshot of calendar with cells highlighted using a different cell background colour.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • You can always open the file manually. On my system, it is located in `C:\Program Files (x86)\Embarcadero\Studio\20.0\source\vcl`. On your system, the version number is certainly different. – Andreas Rejbrand Jul 28 '20 at 18:27
  • hey i have an issue normally how I would access the calendar class by right-clicking and then clicking find declaration but when I do that it says unable to locate calendar.pas i actally want to highlight the text and change the cell color to red from a database i actually found a question like that but it was on mobile and a different version of Delphi [link](https://stackoverflow.com/questions/33632022/how-to-change-cell-color-in-a-tcalendar-component-in-delphi) im sorry i hav no idea on how to use an interposer class: i actually added it under type and the 'Vcl.Samples.'GotHighlighted – Heartfire 12 Jul 28 '20 at 18:30
  • How would i further color the cell red – Heartfire 12 Jul 28 '20 at 18:32
  • Set `Canvas.Brush.Color := clRed` and use `TCanvas.FillRect` to fill the cell. Do this before you draw the text (otherwise you will hide your text with the red rectangle). – Andreas Rejbrand Jul 28 '20 at 18:35
  • thanks you really helped me out any idea on how to change the cell color – Heartfire 12 Jul 28 '20 at 18:40
  • hey when i copy and paste the code then try to save it delphi gives me and error and says unable to create back up folder – Heartfire 12 Jul 28 '20 at 18:53
  • Are you trying to save files to `C:\Program Files (x86)\...`? That's not possible. Windows won't let you access these protected folders. You should save your code in your personal folders, e.g. `C:\Users\Andreas Rejbrand\Documents` in my case. – Andreas Rejbrand Jul 28 '20 at 18:58
  • @AndreasRejbrand This is exactly what I need, but i don't understand it.. I will have to look at this for awhile unless someone has a quicker way to explain how I add this to my project.. I just want all days before TODAY() grey background and option to add other specific dates that will get grey background – Glen Morse Aug 25 '23 at 18:28
  • I guess the one i need is for FMX.Calendar.pas Because i dont see a draw option in the source for TCalendar.pas – Glen Morse Aug 25 '23 at 19:26
  • Why use a `TStringGrid` for a calendar, instead of using [`TMonthCalendar`](https://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TMonthCalendar)? It has an [`OnGetMonthBoldInfo`](https://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TCommonCalendar.OnGetMonthBoldInfo) event for highlighting dates – Remy Lebeau Aug 25 '23 at 21:10