日付の数値の生成

提供: RAD Studio
移動先: 案内検索

日付の追跡 への移動


カレンダーに数値を表示する際に、いくつか考慮しなければならない点があります。1 か月の日数は、その月が何月か、その年がうるう年かどうかによって、変わります。さらに、その月が何曜日から始まるかも、年と月によってさまざまです。IsLeapYear 関数を使用すると、その年がうるう年かどうかを判断できます。SysUtils ユニットの MonthDays 配列を使用すると、その月の日数を取得できます。

うるう年と月の日数の情報を手に入れたら、個々の日付をグリッドのどの部分に表示するかを計算します。この計算は、その月が何曜日から始まるかに基づいて行います。

日付を表示するセルごとにその月のオフセット数が必要なので、年または月を変更したときにこの数を一度だけ計算し、その後は必要になるたびにそれを参照します。この値はクラス フィールドに格納し、日付が変わるたびにフィールドを更新します。

日付を適切なセルに表示するには:

  1. 月のオフセット数のフィールドと、そのフィールド値を更新するメソッドとをオブジェクトに追加します。
 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
}
  1. 日付が変化したときにその更新用メソッドを呼び出すステートメントを、コンストラクタ、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
    }
    
  2. セルの行と列の座標を渡すと日の数値を返すメソッドを、カレンダーに追加します。

     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 の宣言を追加するのを忘れないでください。
  3. 日付を表示する場所を計算できるようになったので、日付を表示するよう 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);
    }
    

これで、カレンダー コンポーネントをインストールし直してフォーム上に配置すると、現在の月の情報が正しく表示されます。

関連項目