日付の数値の生成
日付の追跡 への移動
カレンダーに数値を表示する際に、いくつか考慮しなければならない点があります。1 か月の日数は、その月が何月か、その年がうるう年かどうかによって、変わります。さらに、その月が何曜日から始まるかも、年と月によってさまざまです。IsLeapYear 関数を使用すると、その年がうるう年かどうかを判断できます。SysUtils ユニットの MonthDays 配列を使用すると、その月の日数を取得できます。
うるう年と月の日数の情報を手に入れたら、個々の日付をグリッドのどの部分に表示するかを計算します。この計算は、その月が何曜日から始まるかに基づいて行います。
日付を表示するセルごとにその月のオフセット数が必要なので、年または月を変更したときにこの数を一度だけ計算し、その後は必要になるたびにそれを参照します。この値はクラス フィールドに格納し、日付が変わるたびにフィールドを更新します。
日付を適切なセルに表示するには:
- 月のオフセット数のフィールドと、そのフィールド値を更新するメソッドとをオブジェクトに追加します。
type TSampleCalendar = class(TCustomGrid) private FMonthOffset: Integer; { storage for the offset } . . . protected procedure UpdateCalendar; virtual; { property for offset access } end; . . . procedure TSampleCalendar.UpdateCalendar; var AYear, AMonth, ADay: Word; FirstDate: TDateTime; { date of the first day of the month } begin if FDate <> 0 then { only calculate offset if date is valid } begin DecodeDate(FDate, AYear, AMonth, ADay); { get elements of date } FirstDate := EncodeDate(AYear, AMonth, 1); { date of the first } FMonthOffset := 2 - DayOfWeek(FirstDate); { generate the offset into the grid } end; Refresh; { always repaint the control } end;
class PACKAGE TSampleCalendar : public TCustomGrid { private: int FMonthOffset; // storage for the offset . . . protected: virtual void __fastcall UpdateCalendar(void); . . . }; void __fastcall TSampleCalendar::UpdateCalendar(void) { unsigned short AYear, AMonth, ADay; TDateTime FirstDate; // date of first day of the month if ((int)FDate != 0) // only calculate offset if date is valid { FDate.DecodeDate(&AYear, &AMonth, &ADay); // get elements of date FirstDate = TDateTime(AYear, AMonth, 1); // date of the first FMonthOffset = 2 - FirstDate.DayOfWeek(); // generate the offset into the grid } Refresh(); // always repaint the control }
日付が変化したときにその更新用メソッドを呼び出すステートメントを、コンストラクタ、SetCalendarDate メソッド、SetDateElement メソッドに追加します。
constructor TSampleCalendar.Create(AOwner: TComponent); begin inherited Create(AOwner); { this is already here } . { other initializations here } . . UpdateCalendar; { set proper offset } end; procedure TSampleCalendar.SetCalendarDate(Value: TDateTime); begin FDate := Value; { this was already here } UpdateCalendar; { this previously called Refresh } end;
procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer); begin . . . FDate := EncodeDate(AYear, AMonth, ADay); { encode the modified date } UpdateCalendar; { this previously called Refresh } end; end; __fastcall TSampleCalendar::TSampleCalendar(TComponent *Owner) : TCustomGrid(Owner) { . . . UpdateCalendar(); } void __fastcall TSampleCalendar::SetCalendarDate(TDateTime Value) { FDate = Value; // this was already here UpdateCalendar(); // this previously called Refresh } void __fastcall TSampleCalendar::SetDateElement(int Index, int Value) { . . . FDate = TDateTime(AYear, AMonth, ADay); // this was already here UpdateCalendar(); // this previously called Refresh }
セルの行と列の座標を渡すと日の数値を返すメソッドを、カレンダーに追加します。
function TSampleCalendar.DayNum(ACol, ARow: Integer): Integer; begin Result := FMonthOffset + ACol + (ARow - 1) * 7; { calculate day for this cell } if (Result < 1) or (Result > MonthDays[IsLeapYear(Year), Month]) then Result := -1; { return -1 if invalid } end;
int __fastcall TSampleCalendar::DayNum(int ACol, int ARow) { int result = FMonthOffset + ACol + (ARow - 1) * 7; // calculate day for this cell if ((result < 1)||(result > MonthDays[IsLeapYear(Year)][Month])) result = -1; // return -1 if invalid return result; }
コンポーネントの型宣言にこの DayNum の宣言を追加するのを忘れないでください。
日付を表示する場所を計算できるようになったので、日付を表示するよう DrawCell を更新します。
procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); var TheText: string; TempDay: Integer; begin if ARow = 0 then { if this is the header row ...} TheText := ShortDayNames[ACol + 1] { just use the day name } else begin TheText := ; { blank cell is the default } TempDay := DayNum(ACol, ARow); { get number for this cell } if TempDay <> -1 then TheText := IntToStr(TempDay); { use the number if valid } end; with ARect, Canvas do TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2, Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText); end;
void __fastcall TSampleCalendar::DrawCell(int ACol, int ARow, const TRect &ARect, TGridDrawState AState) { String TheText; int TempDay; if (ARow == 0) // this is the header row TheText = ShortDayNames[ACol + 1]; // just use the day name else { TheText = ""; // blank cell is the default TempDay = DayNum(ACol, ARow); // get number for this cell if (TempDay != -1) TheText = IntToStr(TempDay); // use the number if valid } Canvas->TextRect(ARect, ARect.Left + (ARect.Right - ARect.Left - Canvas->TextWidth(TheText)) / 2, ARect.Top + (ARect.Bottom - ARect.Top - Canvas->TextHeight(TheText)) / 2, TheText); }
これで、カレンダー コンポーネントをインストールし直してフォーム上に配置すると、現在の月の情報が正しく表示されます。