⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wqcalendar.pas

📁 一款不错的年历控件
💻 PAS
字号:
unit WqCalendar;

interface

uses Classes, Controls, Messages, Windows, Forms, Graphics, StdCtrls,
  Grids, SysUtils;

type
  TDayOfWeek = 0..6;

  TWqCalendar = class(TCustomGrid)
  private
    FDate: TDateTime;
    FMonthOffset: Integer;
    FOnChange: TNotifyEvent;
    FReadOnly: Boolean;
    FStartOfWeek: TDayOfWeek;
    FUpdating: Boolean;
    FUseCurrentDate: Boolean;
    function GetCellText(ACol, ARow: Integer): string;
    function GetDateElement(Index: Integer): Integer;
    procedure SeTWqCalendarDate(Value: TDateTime);
    procedure SetDateElement(Index: Integer; Value: Integer);
    procedure SetStartOfWeek(Value: TDayOfWeek);
    procedure SetUseCurrentDate(Value: Boolean);
    function StoreCalendarDate: Boolean;
  protected
    procedure Change; dynamic;
    procedure ChangeMonth(Delta: Integer);
    procedure Click; override;
    function DaysPerMonth(AYear, AMonth: Integer): Integer; virtual;
    function DaysThisMonth: Integer; virtual;
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
    function IsLeapYear(AYear: Integer): Boolean; virtual;
    function SelectCell(ACol, ARow: Longint): Boolean; override;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property CalendarDate: TDateTime  read FDate write SeTWqCalendarDate stored StoreCalendarDate;
    property CellText[ACol, ARow: Integer]: string read GetCellText;
    procedure NextMonth;
    procedure NextYear;
    procedure PrevMonth;
    procedure PrevYear;
    function YearCyclical:String;
    function MonthCyclical:String;
    function DayCyclical:String;
    function HourCyclical(ADate :TDateTime):String;
    function Animals:String;
    function SolarTerm:String;
    function HolDay:String;
    function NLYear:Integer;
    function NLMonth:Integer;
    function NLDay:Integer;
    procedure UpdateCalendar; virtual;
  published
    property Align;
    property Anchors;
    property BorderStyle;
    property Color;
    property Constraints;
    property Ctl3D;
    property Day: Integer index 3  read GetDateElement write SetDateElement stored False;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property GridLineWidth;
    property Month: Integer index 2  read GetDateElement write SetDateElement stored False;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
    property ShowHint;
    property StartOfWeek: TDayOfWeek read FStartOfWeek write SetStartOfWeek;
    property TabOrder;
    property TabStop;
    property UseCurrentDate: Boolean read FUseCurrentDate write SetUseCurrentDate default True;
    property Visible;
    property Year: Integer index 1  read GetDateElement write SetDateElement stored False;
    property OnClick;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnStartDock;
    property OnStartDrag;
  end;

procedure Register;

implementation

uses DateCn;

var
  DrawBitmap: TBitmap;
  UserCount: Integer;

procedure UsesBitmap;
begin
  if UserCount = 0 then
    DrawBitmap := TBitmap.Create;
  Inc(UserCount);
end;

procedure ReleaseBitmap;
begin
  Dec(UserCount);
  if UserCount = 0 then DrawBitmap.Free;
end;

function Max(X, Y: Integer): Integer;
begin
  Result := Y;
  if X > Y then Result := X;
end;

procedure WriteText(ACanvas: TCanvas; ARect: TRect; DX, DY ,DT: Integer;
  const Text: string; Alignment: TAlignment; MultiRows : boolean);
const
  AlignFlags : array [TAlignment] of Integer =
    ( DT_LEFT  or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
      DT_RIGHT  or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
      DT_CENTER  or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX );
var
  B, R: TRect;
  I: Cardinal;
  Align : integer;
begin
  Align := DT_VCENTER;
  if not ( MultiRows ) then
    Align := Align + DT_WORDBREAK;

  I := ColorToRGB(ACanvas.Brush.Color);
  if GetNearestColor(ACanvas.Handle, I) = I then
  begin                       { Use ExtTextOut for solid colors }
    ACanvas.FillRect(ARect);
    with ARect do begin
      Left := Left + DX;
      Right := Right - DX;
      Top := Top + DT;
    end;
    DrawText(ACanvas.Handle, PChar(Text), Length(Text), ARect,
             AlignFlags[Alignment] + Align);
  end
  else begin                  { Use FillRect and Drawtext for dithered colors }
    DrawBitmap.Canvas.Lock;
    try
      with DrawBitmap, ARect do { Use offscreen bitmap to eliminate flicker and }
      begin                     { brush origin tics in painting / scrolling.    }
        Width := Max(Width, Right - Left);
        Height := Max(Height, Bottom - Top);
        R := Rect( DX, DY, Right - Left - 1, Bottom - Top);
        B := Rect(0, 0, Right - Left, Bottom - Top);
      end;
      with DrawBitmap.Canvas do
      begin
        Font := ACanvas.Font;
        Font.Color := ACanvas.Font.Color;
        Brush := ACanvas.Brush;
        Brush.Style := bsSolid;
        FillRect(B);
        SetBkMode(Handle, TRANSPARENT);
        DrawText(Handle, PChar(Text), Length(Text), R,
            AlignFlags[Alignment] + Align );
      end;
      ACanvas.CopyRect(ARect, DrawBitmap.Canvas, B);
    finally
      DrawBitmap.Canvas.Unlock;
    end;
  end;
end;


constructor TWqCalendar.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  { defaults }
  //
  UsesBitmap;
  //
  FUseCurrentDate := True;
  FixedCols := 0;
  FixedRows := 1;
  ColCount := 7;
  RowCount := 7;
  Height := 240;
  Width := 300;
  ScrollBars := ssNone;
  Options := Options - [goRangeSelect] + [goDrawFocusSelected];
  Font.Name := '细明体';
  FDate := Date;
  UpdateCalendar;
end;

destructor TWqCalendar.Destroy;
begin
 ReleaseBitmap;
 inherited Destroy;
end;

procedure TWqCalendar.Change;
begin
  if Assigned(FOnChange) then FOnChange(Self);
end;

procedure TWqCalendar.Click;
var
  TheCellText: string;
begin
  inherited Click;
  TheCellText := CellText[Col, Row];
  if TheCellText <> '' then Day := StrToInt(TheCellText);
end;

function TWqCalendar.IsLeapYear(AYear: Integer): Boolean;
begin
  Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or (AYear mod 400 = 0));
end;

function TWqCalendar.DaysPerMonth(AYear, AMonth: Integer): Integer;
const
  DaysInMonth: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
begin
  Result := DaysInMonth[AMonth];
  if (AMonth = 2) and IsLeapYear(AYear) then Inc(Result); { leap-year Feb is special }
end;

function TWqCalendar.DaysThisMonth: Integer;
begin
  Result := DaysPerMonth(Year, Month);
end;

procedure TWqCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
  TheText,NText,HolDay    : string;
  TheDate           : TDate;
  R                 : TRect;
  GColor,NColor     : TColor;
begin
  TheText := CellText [ACol, ARow];
  NText := '';
  HolDay := '';

  GColor := Canvas.Font.Color;
  NColor := Canvas.Font.Color;

  if (TheText <> '') and (ARow > 0) and
     (Year >= 1901) and (Year <= 2050) then
    begin
      TheDate := EncodeDate (Year, Month, StrToInt (TheText));
      if (DayOfWeek (TheDate) = 1) then
        GColor := clRed;
      if (DayOfWeek (TheDate) = 7) then
        GColor := $0000BF00;;
      if (CnDayOfDate (TheDate) = 1) then
        begin
          NText := CnMonthOfDateCH (TheDate);
          NColor := clRed;

        end
      else
        begin
          NText := CnDayOfDateCH(TheDate);
        end;
      if (CnMonthOfDate(TheDate) =1) and (CnDayOfDate(TheDate) in [2,3]) then
        NColor := clRed;

      //  HolDay := GetLunarHolDay(TheDate);
//      HolDay := CnDayOfDateJr(TheDate);
      HolDay := GetSolarTerm(TheDate);
      if HolDay <> '' then
      begin
        NColor := clRed;
      end;

      if HolDay <> '' then NText := HolDay;

    end;

  with ARect,Canvas do begin
    if  ARow = 0 then
      begin
      Canvas.Font.Size := 9;

      WriteText(Canvas, ARect, 2,2,(ARect.Bottom - ARect.Top - TextHeight(TheText)) div 2, TheText, taCenter, True);
      end
    else
      begin
      Canvas.Font.Size := 14;
      Canvas.Font.Color := GColor;
      WriteText(Canvas, ARect, 2,2,0, TheText, taCenter, True);
      R := ARect;
      R.Top := R.Top + Round(((R.bottom - R.Top + 1) / 2)) + 2;
      Canvas.Font.Size := 9;
      Canvas.Font.Color := NColor;
      WriteText(Canvas, R, 2,2,0, NText, taCenter, True);
      end;
  end;

end;

function TWqCalendar.GetCellText(ACol, ARow: Integer): string;
var
  DayNum: Integer;
begin
  if ARow = 0 then  { day names at tops of columns }
    Result := ShortDayNames[(StartOfWeek + ACol) mod 7 + 1]
  else
  begin
    DayNum := FMonthOffset + ACol + (ARow - 1) * 7;
    if (DayNum < 1) or (DayNum > DaysThisMonth) then Result := ''
    else Result := IntToStr(DayNum);
  end;
end;

function TWqCalendar.SelectCell(ACol, ARow: Longint): Boolean;
begin
  if ((not FUpdating) and FReadOnly) or (CellText[ACol, ARow] = '') then
    Result := False
  else Result := inherited SelectCell(ACol, ARow);
end;

procedure TWqCalendar.SeTWqCalendarDate(Value: TDateTime);
begin
  FDate := Value;
//  UpdateCalendar;
  Change;
end;

function TWqCalendar.StoreCalendarDate: Boolean;
begin
  Result := not FUseCurrentDate;
end;

function TWqCalendar.GetDateElement(Index: Integer): Integer;
var
  AYear, AMonth, ADay: Word;
begin
  DecodeDate(FDate, AYear, AMonth, ADay);
  case Index of
    1: Result := AYear;
    2: Result := AMonth;
    3: Result := ADay;
    else Result := -1;
  end;
end;

procedure TWqCalendar.SetDateElement(Index: Integer; Value: Integer);
var
  AYear, AMonth, ADay: Word;
begin
  if Value > 0 then
  begin
    DecodeDate(FDate, AYear, AMonth, ADay);
    case Index of
      1: if AYear <> Value then AYear := Value else Exit;
      2: begin
          if (Value <= 12) and (Value <> AMonth) then AMonth := Value else Exit;
          if Day > DaysPerMonth(AYear,AMonth) then ADay := DaysPerMonth(AYear,AMonth);
         end;
      3: if (Value <= DaysThisMonth) and (Value <> ADay) then ADay := Value else Exit;
      else Exit;
    end;


    FDate := EncodeDate(AYear, AMonth, ADay);
    FUseCurrentDate := False;

    if Index in [1,2] then
      UpdateCalendar;
    Change;
  end;
end;

procedure TWqCalendar.SetStartOfWeek(Value: TDayOfWeek);
begin
  if Value <> FStartOfWeek then
  begin
    FStartOfWeek := Value;
    UpdateCalendar;
  end;
end;

procedure TWqCalendar.SetUseCurrentDate(Value: Boolean);
begin
  if Value <> FUseCurrentDate then
  begin
    FUseCurrentDate := Value;
    if Value then
    begin
      FDate := Date; { use the current date, then }
      UpdateCalendar;
    end;
  end;
end;

{ Given a value of 1 or -1, moves to Next or Prev month accordingly }
procedure TWqCalendar.ChangeMonth(Delta: Integer);
var
  AYear, AMonth, ADay: Word;
  NewDate: TDateTime;
  CurDay: Integer;
begin
  DecodeDate(FDate, AYear, AMonth, ADay);
  CurDay := ADay;
  if Delta > 0 then ADay := DaysPerMonth(AYear, AMonth)
  else ADay := 1;
  NewDate := EncodeDate(AYear, AMonth, ADay);
  NewDate := NewDate + Delta;
  DecodeDate(NewDate, AYear, AMonth, ADay);
  if DaysPerMonth(AYear, AMonth) > CurDay then ADay := CurDay
  else ADay := DaysPerMonth(AYear, AMonth);
  CalendarDate := EncodeDate(AYear, AMonth, ADay);
  UpdateCalendar;
end;

procedure TWqCalendar.PrevMonth;
begin
  ChangeMonth(-1);
end;

procedure TWqCalendar.NextMonth;
begin
  ChangeMonth(1);
end;

procedure TWqCalendar.NextYear;
begin
  if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
  Year := Year + 1;
end;

procedure TWqCalendar.PrevYear;
begin
  if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
  Year := Year - 1;
end;

procedure TWqCalendar.UpdateCalendar;
var
  AYear, AMonth, ADay: Word;
  FirstDate: TDateTime;
begin
  FUpdating := True;
  try
    DecodeDate(FDate, AYear, AMonth, ADay);
    FirstDate := EncodeDate(AYear, AMonth, 1);
    FMonthOffset := 2 - ((DayOfWeek(FirstDate) - StartOfWeek + 7) mod 7); { day of week for 1st of month }
    if FMonthOffset = 2 then FMonthOffset := -5;
    MoveColRow((ADay - FMonthOffset) mod 7, (ADay - FMonthOffset) div 7 + 1,
      False, False);
    Invalidate;
  finally
    FUpdating := False;
  end;
end;

procedure TWqCalendar.WMSize(var Message: TWMSize);
var
  GridLines: Integer;
  iTitleHeight : Integer;
begin
  GridLines := 6 * GridLineWidth;
  DefaultColWidth := (Message.Width - GridLines) div 7;
  DefaultRowHeight := (Message.Height - GridLines) div 7;
  iTitleHeight := DefaultRowHeight div 2;
  DefaultRowHeight := DefaultRowHeight + iTitleHeight mod 7 ;
  RowHeights[0] := iTitleHeight;
end;

function TWqCalendar.YearCyclical:String;
begin
  Result := GetCyclical(FDate,wqYear);
end;


function TWqCalendar.MonthCyclical:String;
begin
  Result := GetCyclical(FDate,wqMonth);
end;

function TWqCalendar.DayCyclical:String;
begin
  Result := GetCyclical(FDate,wqDay);
end;

function TWqCalendar.HourCyclical(ADate :TDateTime):String;
begin
  Result := GetCyclical(ADate,wqHour);
end;


function TWqCalendar.Animals:String;
begin
  Result := GetAnimals(NLYear);
end;

function TWqCalendar.SolarTerm:String;
begin
  Result := GetSolarTerm(FDate);
end;

function TWqCalendar.HolDay:String;
begin
  //
end;

function TWqCalendar.NLYear:Integer;
begin
  Result := CnYearOfDate(FDate);
end;

function TWqCalendar.NLMonth:Integer;
begin
  Result := CnMonthOfDate(FDate);
end;

function TWqCalendar.NLDay:Integer;
begin
  Result := CnDayOfDate(FDate);
end;


procedure Register;
begin
  RegisterComponents ('WQ Library', [TWqCalendar]);
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -