본문 바로가기

프로그래밍/Delphi

TMonthCalendar의 특정날짜 마크하기

1. 개요

  TDateTimePicker 또는 TMonthCalendar의 월력을 Open 했을 때 특정 지정된 날짜를 마킹하여 특정한 목적으로 사용자에게 알려 주어야 하는 경우가 있다. 기본 컴포넌트인 TMonthCalendar를 이용하여 원하는 날짜들에 마킹(볼드체)하는 방법을 알아보자

 

2. TMonthCalendar의 "OnGetMonthInfo" 이벤트

  이 이벤트는 TMonthCalendar 컴포넌트에 새로운 월(Month)가 Display될 때 호출 되는데 화면에 노출되는 일자에 대한 특정한 속성을 초기화 하고자 할 때 사용된다.(예를 들어 일요일 토요일, 국경일 등에 대한 표식이 필요할 경우 등)

  이 이벤트 호출을 통하여 특정한 날짜를 굵게(Bold)로 표시할 수 있다. 

// 표시된 달력의 1일, 8일을 굵게 표시한다.

 

     BoldDays([1,8], MonthBoldInfo);

 

  or

 

     unsigned bolddays[2] = {1,2};
     BoldDays(bolddays, MonthBoldInfo);

 

3. 매월 표시해야 할 사용자 지정일자가 다른 경우에 대한 처리

 

  위에서 처럼 처리하면 모든 월(Month)에 동일한 날짜가 굵게 표시 되는데 일반적으로 스케쥴 관리와 같이 매월 사용자가 지정한 특정한 날이 다를 수 있는데 이를 위해서는 좀 다르게 처리해 주어야 한다.

 

  OnGetMonthInfo이벤트는 달력의 월이 바뀔 때마다 발생하게 되는데 매번 3번씩 호출 된다.  첫 번째는 이전달, 두 번째는 이번달 그리고 세 번째는 다음달이 Month 파라미터에 넘겨져서 호출 된다. 따라서 표시하고자 하는 현재 월만 선택하여 현재 선택된 월의 마킹 날자 리스트를 얻고 표시해 주어야 한다. 여기서 주의 할 점은 Month(월)만 넘어오기 때문에 연도를 유추 해 내야 한다는 것인데 이를 위해서도 약간의 코딩이 필요하다.

 

4. TMonthCalendar의 "BoldDays" 함수

  파라미터인 ABoldDays 배열에 담긴 지정된 날짜들을 1~31 비트로 매핑하여 DWORD 값으로 MonthBoldInfo에  리턴한다

예)  MonthCalendar1.BoldDays([3,21,28],MonthBoldInfo); { Day 3, 21, 28 }

 

 

5. TEST 프로그램 결과

 

 

 

6. TEST 프로그램 소스

 

 

unit uTestMain;
{** Usage : TMonthCalendar에 월별 특정한 날짜들을 마킹(Bold) 해 본다
 *  Created By niceondal, 2014-12-08
 *}
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, CommCtrl;

type
  TForm1 = class(TForm)
    calendar: TMonthCalendar;
    procedure calendarGetMonthInfo(Sender: TObject; Month: Cardinal;
      var MonthBoldInfo: Cardinal);
  private
    function SetExistDateToCalenar(ADate: TDateTime): DWORD;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TArrBold = array of DWORD;
const
  testBoldDates : array[1..12] of array[0..6] of DWORD =
    ((1,3,4,6,7,8,9),
     (1,2,3,4,5,9,10),
     (4,6,8,15,19,20,21),
     (11,22,23,24,25,29,30),
     (11,12,13,14,15,19,20),
     (5,10,15,20,25,26,27),
     (2,4,6,8,10,12,14),
     (12,14,16,18,20,22,24),
     (1,3,5,7,5,9,10),
     (4,8,12,16,20,24,28),
     (1,2,4,8,16,21,22),
     (1,3,5,16,20,24,28)
     );

var
  _LastMonth : integer;
  _LastYear : integer;
  _LastTickCount : Integer;
  _CallCountOfMonthInfo : Integer;

function CheckTickCount:Boolean;
var
  C:Cardinal;
begin
  C:=GetTickCount;
  Result: = _LastTickCount+100 < C;  // Less than 0,1s allowed between events
  _LastTickCount := C;
end;

// 해당날짜가 속한 월의 마킹 날자 배열을 구하여 MonthCalendar에 적용시킨다
// 연도 옵션을 추가하여 년/월 별 마킹을 추가로 구현해보세요(....)
function TForm1.SetExistDateToCalenar(ADate: TDateTime) : DWORD;
var
  DayStates: array[0..2] of DWORD;
  mon : DWORD;
  arrDate : array of DWORD;
  yy, mm, dd : Word;
begin
  result := 0;

  DecodeDate(ADate, yy, mm, dd);
  //여기에서 연도/월별 Bold시킬 배열을 얻도록 한다.
  //연도(yy)에 따라 다를 경우도 처리해 주어야 함
  if Length(testBoldDates[mm])>0 then
  begin
    calendar.BoldDays(testBoldDates[mm], mon);
    DayStates[1] := mon;
    result := mon;
  end
  else
  begin
    DayStates[1] := 0;
    result := 0;
  end;

  DayStates[0] := 0;
  DayStates[2] := 0;
  SendMessage(calendar.Handle, MCM_SETDAYSTATE, 3, longint(@DayStates));
end;


procedure TForm1.calendarGetMonthInfo(Sender: TObject; Month: Cardinal;
  var MonthBoldInfo: Cardinal);
var
  dd : TDateTime;
  tmp : string;
  Year,Mon,Day,YearMonth:Word;
begin
  // 연도를 계산하기 위한 모듈
  if CheckTickCount Or ((Month<>_LastMonth+1) And Not ((_LastMonth=12) And (Month=1))) then
  begin
   DecodeDate((Sender As TMonthCalendar).Date,Year,Mon,Day);
   if Month>Mon then Dec(Year);
  end Else
  begin
   if _LastMonth=12
    then Year:=_LastYear+1
    Else Year:=_LastYear;
  end;
  _LastMonth:=Month;
  _LastYear:=Year;

  // 현재 노출되어져야 할 월을 판별하기 위한 모듈
  Inc(_CallCountOfMonthInfo);
  if _CallCountOfMonthInfo=2 then //현재 표시될 월에 ㅎ당
  begin
    // Calendar가 다음월 다음해 등으로 스크롤 되는 경우 현재 표시되고 있는 년/월을 정확히 알아낸다.
    tmp := Format('%4d-%2d-01', [Year, Month]);
    tmp := StringReplace(tmp, ' ', '0', []);
    //여기에 해당 월별 Bold로 표시핼 배열 정보를 얻도록 한다.
    MonthBoldInfo := SetExistDateToCalenar(StrToDate(tmp));
  end
  else
    MonthBoldInfo := 0;

  if _CallCountOfMonthInfo = 3 then   //초기화(3번씩 호출되는 마지막번째 호출됨)
    _CallCountOfMonthInfo := 0;
end;

end.

 

7. 간단하게 현재 달력에 특정한 날짜들을 간단히 Bold로 지정하는 방법은 다음과 같이 하면 된다.

 

 

procedure TForm1.btnTestClick(Sender: TObject);
var
  monInfo : Cardinal;
  DayStates: array[0..2] of DWORD;
begin
  calendar.BoldDays([1,2,3,4,5,6,7,8,9], monInfo);
  DayStates[0] := 0;
  DayStates[1] := monInfo;
  DayStates[2] := 0;
  SendMessage(calendar.Handle, MCM_SETDAYSTATE, 3, longint(@DayStates));
end;