Die Anzahl der Tage im Monat generieren
Nach oben zu Das Datum festlegen
Beim Füllen des Kalenders mit Werten gibt es einiges zu beachten: Die Anzahl der Tage im Monat hängt vom jeweiligen Monat ab. Beim Monat Februar ist wichtig, ob es sich um ein Schaltjahr handelt. Außerdem beginnen die Monate an unterschiedlichen Wochentagen, was vom jeweiligen Monat bzw. Jahr abhängt. Mit der Funktion IsLeapYear kann ermittelt werden, ob es sich um ein Schaltjahr handelt. Das Array MonthDays in der Unit-Datei SysUtils liefert die Anzahl der Monatstage.
Nachdem nun die Tage pro Monat und die Schaltjahre verfügbar sind, kann berechnet werden, in welche Zellen die einzelnen Datumswerte eingefügt werden müssen. Die Berechnung basiert auf dem Wochentag, mit dem der Monat beginnt.
Sie benötigen nun eine Zahl, die für jede Zelle die Differenz zum Monatsersten angibt. Am sinnvollsten ist es, diese Zahl nur einmal beim Wechsel des Monats bzw. des Jahres zu berechnen und später auf sie Bezug zu nehmen. Der Wert wird in einem Klassenfeld gespeichert, das bei jeder Datumsänderung aktualisiert wird.
Zum Einfügen der Tage in die korrekten Zellen sind folgende Schritte erforderlich:
- Fügen Sie dem Objekt ein Feld für die Differenz zum Monatsersten hinzu. Außerdem benötigen Sie eine Methode, die den Wert dieses Feldes aktualisiert:
type TSampleCalendar = class(TCustomGrid) private FMonthOffset: Integer; { Speichert die Differenz zum Monatsersten } . . . protected procedure UpdateCalendar; virtual; { Eigenschaft für den Zugriff auf FMonthOffset } end; . . . procedure TSampleCalendar.UpdateCalendar; var AYear, AMonth, ADay: Word; FirstDate: TDateTime { Datum des ersten Tages im Monat } begin if FDate <> 0 then { Bei gültigem Datum nur die Differenz berechnen } begin DecodeDate(FDate, AYear, AMonth, ADay); { Elemente des Datums ermitteln } FirstDate := EncodeDate(AYear, AMonth, 1); { Daten für den Monatsersten } FMonthOffset := 2 - DayOfWeek(FirstDate) { Differenz auf das Gitter übertragen } end; Refresh; { Anzeige immer aktualisieren } end;
class PACKAGE TSampleCalendar : public TCustomGrid { private: int FMonthOffset;// Speicherplatz für die Differenz . . . protected: virtual void __fastcall UpdateCalendar(void); . . . }; void __fastcall TSampleCalendar::UpdateCalendar(void) { unsigned short AYear, AMonth, ADay; TDateTime FirstDate; // Datum des ersten Monatstages if ((int)FDate != 0) // Differenz nur berechnen, wenn Datum gültig ist { FDate.DecodeDate(&AYear, &AMonth, &ADay); // Datumselemente abrufen FirstDate = TDateTime(AYear, AMonth, 1); // Datum des Monatsersten FMonthOffset = 2 - FirstDate.DayOfWeek(); // Verschiebung im Gitter generieren } Refresh(); // Steuerelement neu aufbauen }
Fügen Sie dem Konstruktor und den Methoden SetCalendarDate und SetDateElement Anweisungen hinzu, die bei jeder Änderung des Datums die neue Aktualisierungsmethode aufrufen:
constructor TSampleCalendar.Create(AOwner: TComponent); begin inherited Create(AOwner); { existiert bereits } . { hier folgen weitere Initialisierungen } . . UpdateCalendar { Korrekte Differenz zum Monatsersten festlegen } end; procedure TSampleCalendar.SetCalendarDate(Value: TDateTime); begin FDate := Value { War bereits vorhanden } UpdateCalendar; { Hat vorher Refresh aufgerufen } end; procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer); begin . . . FDate := EncodeDate(AYear, AMonth, ADay); { Geändertes Datum codieren } UpdateCalendar; { Hat vorher Refresh aufgerufen } end; end; __fastcall TSampleCalendar::TSampleCalendar(TComponent *Owner) : TCustomGrid(Owner) { . . . UpdateCalendar(); } void __fastcall TSampleCalendar::SetCalendarDate(TDateTime Value) { FDate = Value; // Das war schon da UpdateCalendar(); // Hier wurde zuvor Refresh aufgerufen } void __fastcall TSampleCalendar::SetDateElement(int Index, int Value) { . . . FDate = TDateTime(AYear, AMonth, ADay); // Das war schon da UpdateCalendar(); // Hier wurde zuvor Refresh aufgerufen }
Fügen Sie dem Kalender eine Methode hinzu, die die Anzahl der Tage zurückgibt, wenn die Zeilen- und Spaltenkoordinaten einer Zelle an sie übergeben werden:
function TSampleCalendar.DayNum(ACol, ARow: Integer): Integer; begin Result := FMonthOffset + ACol + (ARow - 1) * 7 { Tag für diese Zelle berechnen } if (Result < 1) or (Result > MonthDays[IsLeapYear(Year), Month]) then Result := -1; { Wenn ungültig, -1 zurückgeben } end;
int __fastcall TSampleCalendar::DayNum(int ACol, int ARow) { int result = FMonthOffset + ACol + (ARow - 1) * 7; // Tag für diese Zelle berechnen if ((result < 1)||(result > MonthDays[IsLeapYear(Year)][Month])) result = -1; // zurückgeben, wenn ungültig return result; }
Vergessen Sie nicht, der Typdeklaration die Deklaration von DayNum hinzuzufügen.
Nachdem nun berechnet werden kann, in welche Zellen die Datumswerte eingefügt werden müssen, können Sie die Methode DrawCell entsprechend aktualisieren:
procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); var TheText: string; TempDay: Integer; begin if ARow = 0 then { In der Kopfzeile ...} TheText := ShortDayNames[ACol + 1] { nur den Tagesnamen anzeigen } else begin TheText := ; { Standardmäßig ist eine Zelle leer } TempDay := DayNum(ACol, ARow); { Zahl für diese Zelle ermitteln } if TempDay <> -1 then TheText := IntToStr(TempDay); { Wenn gültig, die Zahl anzeigen } 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)// Dies ist die Kopfzeile TheText = ShortDayNames[ACol + 1]; // Nur den Tagesnamen verwenden else { TheText = ""; // blank cell is the default TempDay = DayNum(ACol, ARow); // Nummer dieser Zelle ermitteln if (TempDay != -1) TheText = IntToStr(TempDay);// Nummer verwenden, wenn gültig } Canvas->TextRect(ARect, ARect.Left + (ARect.Right - ARect.Left - Canvas->TextWidth(TheText)) / 2, ARect.Top + (ARect.Bottom - ARect.Top - Canvas->TextHeight(TheText)) / 2, TheText); }
Wenn der Kalender nun neu installiert und in ein Formular eingefügt wird, zeigt er die korrekten Informationen für den aktuellen Monat an.