Die Anzahl der Tage im Monat generieren

Aus RAD Studio
Wechseln zu: Navigation, Suche

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:

  1. 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
}
  1. 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
    }
    
  2. 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.

  3. 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.

Siehe auch