Génération des numéros de jours
Remonter à Suivi de la date
Insérer les numéros des jours dans le calendrier nécessite plusieurs considérations. Le nombre de jours dans le mois dépend à la fois du mois et de l'année. Le jour de la semaine qui débute le mois dépend aussi du mois et de l'année. Utilisez la fonction IsLeapYear pour déterminer si l'année est bissextile. Utilisez le tableau MonthDays dans l'unité SysUtils pour obtenir le nombre de jours dans le mois.
Une fois récupérées les informations concernant les années bissextiles et le nombre de jours par mois, vous pouvez calculer l'endroit de la grille où s'insère chaque date. Le calcul dépend du premier jour du mois.
Comme vous devez considérer le décalage du premier jour du mois, par rapport à l'origine de la grille, pour chaque cellule à remplir, le meilleur choix consiste à calculer ce nombre après chaque changement de mois ou d'année, et de s'y reporter à chaque fois. Vous pouvez stocker cette valeur dans un champ de classe, puis mettre à jour ce champ à chaque modification de la date.
Pour remplir les cellules avec les numéros de jour appropriés, procédez de la façon suivante :
- Ajoutez à la classe un champ décalage du premier jour du mois, ainsi qu'une méthode pour mettre à jour la valeur du champ :
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 }
Ajoutez les instructions au constructeur et aux méthodes SetCalendarDate et SetDateElement qui appellent la nouvelle méthode de mise à jour à chaque changement de date :
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 }
Ajoutez une méthode au calendrier renvoyant le numéro du jour à partir des coordonnées ligne/colonne d'une cellule qui lui sont transmises :
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; }
Pensez à ajouter la déclaration de DayNum à la déclaration de type du composant.
Vous pouvez désormais calculer l'endroit où s'affichent les dates, et mettre à jour DrawCell pour remplir les cellules :
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); }
Si maintenant vous réinstallez le composant calendrier et le placez dans une fiche, les informations correspondant au mois en cours apparaîtront.