Search code examples
delphicalendardelphi-2009

How to get the year when bolding month days in a TMonthCalendar?


I have a log that use the Calendar and I want to bold the days that have recorded info. I have them in an 3D array TDiaryLog = array[1900..2399] of array[1..12] of array[1..31] of POneDay;. But in the OnGetMonthInfo, when I must build the list with the bold days, it gives me only the Month, and not the Year. How should I know for what month I must pass the day if I don't have the year ? When December is displayed as the current month in the Calendar, there are a few days shown from the January next year !

procedure TMainForm.CalendarGetMonthInfo(Sender: TObject; Month: Cardinal;
  var MonthBoldInfo: Cardinal);
begin
end;

Solution

  • I made a new component where I intercepted the MCN_GETDAYSTATE message and I extracted the year too from the message info... It was there all the time, but Delphi decided that year is not useful.

      TOnGetMonthInfoExEvent = procedure(Sender: TObject; Year, Month: Word;
        var MonthBoldInfo: LongWord) of object;
    
      TNewMonthCalendar = class(TMonthCalendar)
      private
        FOnGetMonthInfoEx: TOnGetMonthInfoExEvent;
        procedure CNNotify(var Msg: TWMNotifyMC); message CN_NOTIFY;
      published
        property OnGetMonthInfoEx: TOnGetMonthInfoExEvent read FOnGetMonthInfoEx write FOnGetMonthInfoEx;
      end;
    
    procedure TNewMonthCalendar.CNNotify(var Msg: TWMNotifyMC);
    var
      I: Integer;
      Month, Year: Word;
      DS: PNMDayState;
      CurState: PMonthDayState;
    begin
     if (Msg.NMHdr.code = MCN_GETDAYSTATE) and Assigned(FOnGetMonthInfoEx) then begin
      DS:= Msg.NMDayState;
      FillChar(DS.prgDayState^, DS.cDayState * SizeOf(TMonthDayState), 0);
      CurState:= DS.prgDayState;
      for I:= 0 to DS.cDayState - 1 do begin
       Year:= DS.stStart.wYear;
       Month:= DS.stStart.wMonth + I;
       if Month > 12 then begin Inc(Year); Dec(Month, 12); end;
       FOnGetMonthInfoEx(Self, Year, Month, CurState^);
       Inc(CurState);
      end;
     end
     else inherited;
    end;
    

    BONUS

    And, as a bonus, you need this to update the changes you made to the bold info of the current month view... because it doesn't work with Invalidate.

    procedure TNewMonthCalendar.RefreshDayState;
    var N: Cardinal;
        Range: array[0..1] of TSystemTime;
        Year, Month: Word;
        States: array of TMonthDayState;
        I: Integer;
    begin
     if not Assigned(FOnGetMonthInfoEx) then Exit;
     N:= SendMessage(Handle, MCM_GETMONTHRANGE, GMR_DAYSTATE, LPARAM(@Range));
     Year:= Range[0].wYear;
     Month:= Range[0].wMonth;
     SetLength(States, N);
     FillChar(States[0], N * SizeOf(TMonthDayState), 0);
     for I:= 0 to N-1 do begin
      FOnGetMonthInfoEx(Self, Year, Month, States[I]);
      Inc(Month);
      if Month > 12 then
       begin Dec(Month, 12); Inc(Year); end;
     end;
     SendMessage(Handle, MCM_SETDAYSTATE, N, LPARAM(@States[0]));
    end;