Putting numbers into the calendar involves several considerations. The number of days in the month depends on which month it is, and whether the given year is a leap year. In addition, months start on different days of the week, dependent on the month and year. Use the IsLeapYear function to determine whether the year is a leap year. Use the MonthDays array in the SysUtils unit to get the number of days in the month.
Once you have the information on leap years and days per month, you can calculate where in the grid the individual dates go. The calculation is based on the day of the week the month starts on.
Because you will need the month-offset number for each cell you fill in, the best practice is to calculate it once when you change the month or year, then refer to it each time. You can store the value in a class field, then update that field each time the date changes.
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 }
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; }
Remember to add the declaration of DayNum to the component's type declaration.
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); }
Now if you reinstall the calendar component and place one on a form, you will see the proper information for the current month.
Copyright(C) 2008 CodeGear(TM). All Rights Reserved.
|
What do you think about this topic? Send feedback!
|