adatectl.pas

来自「delphi编程控件」· PAS 代码 · 共 1,516 行 · 第 1/3 页

PAS
1,516 行
字号
        begin
          R.Right := R.Left;
          R.Left := 0;
          SelectColor(MonthNameSelected);
          FillRect(R);
        end;
        if I = DaysPerMonth(AYear, AMonth) then
        begin
          R.Left := R.Right;
          R.Right := ClientWidth;
          SelectColor(IsSelected(ADate) and IsSelected(ADate + 1));
          FillRect(R);
        end;
      end;
    end;
  end;

begin
  inherited Paint;

  FirstYear := 0;
  FirstMonth := 0;
  LastYear := 0;
  LastMonth := 0;
  LastRow := 0;
  DecodeDate(FDate, Year, Month, Day);
  if TopRow >= 0 then
    repeat
      Inc(LastRow, MonthHeight(Year, Month, True));
      if (LastRow > TopRow) and (FirstMonth = 0) then
      begin
        FirstYear := Year;
        FirstMonth := Month;
      end;
      if LastRow > TopRow + VisibleRowCount then
      begin
        LastYear := Year;
        LastMonth := Month;
        Break;
      end;
      IncMonth(Year, Month);
    until False
  else
    repeat
      DecMonth(Year, Month);
      Dec(LastRow, MonthHeight(Year, Month, True));
      if LastRow <= TopRow then
      begin
        FirstYear := Year;
        FirstMonth := Month;
        repeat
          Inc(LastRow, MonthHeight(Year, Month, True));
          if LastRow > TopRow + VisibleRowCount then
          begin
            LastYear := Year;
            LastMonth := Month;
            Break;
          end;
          IncMonth(Year, Month);
        until False;
        Break;
      end;
    until False;
  Year := FirstYear;
  Month := FirstMonth;

  Bitmap := TBitmap.Create;
  Bitmap.Width := ClientWidth;
  Bitmap.Height := ClientHeight;
  with Bitmap.Canvas do
  begin
    Font := Self.Font;
    repeat
      DrawMonth(Year, Month);
      if (Year = LastYear) and (Month = LastMonth) then Break;
      IncMonth(Year, Month);
    until False;
  end;
  Canvas.Draw(0, 0, Bitmap);
  Bitmap.Free;

  with ScrollInfo do
  begin
    cbSize := SizeOf(ScrollInfo);
    fMask := SIF_ALL;
    nPage := VisibleRowCount;
    nMin := TopRow - 70;
    nMax := TopRow + 70 + nPage;
    nPos := TopRow;
  end;
  SetScrollInfo(Handle, SB_VERT, ScrollInfo, True);
end;

function TCustomAutoDateControl.PosOfDate(ADate: TDateTime): TPoint;
begin
  with Result do
  begin
    X := ColOfDate(ADate) * ColWidth - ColWidth div 2;
    Y := (RowOfDate(ADate) - TopRow) * RowHeight + RowHeight div 2;
  end;
end;

function TCustomAutoDateControl.RowOfDate(ADate: TDateTime): Integer;
var
  AYear, AMonth, ADay: Word;
begin
  DecodeDate(ADate, AYear, AMonth, ADay);
  Result := MonthTop(AYear, AMonth) + 1;
  if ADay > FirstWeekDays(AYear, AMonth) then
    Inc(Result, 1 + (ADay - FirstWeekDays(AYear, AMonth) - 1) div 7);
end;

procedure TCustomAutoDateControl.SetParent(AParent: TWinControl);
begin
  inherited SetParent(AParent);
  SetSize;
end;

procedure TCustomAutoDateControl.SetSize;
begin
  if (not IsPopup and (Parent = nil)) or
    (csDestroying in ComponentState) then Exit;
  ColWidth := 5 + Canvas.TextWidth('00') + 5;
  RowHeight := 2 + Canvas.TextHeight('00') + 2;
  Width := GetWidth;
  RecreateWnd;
end;

procedure TCustomAutoDateControl.Hide;
begin
  if IsPopup then DestroyHandle
  else inherited Hide;
  if Assigned(FOnHide) then FOnHide(Self);
end;

procedure TCustomAutoDateControl.Show;
begin
  SetSize;
  if IsPopup then
  begin
    ShowWindow(Handle, SW_SHOW);
    SendMessage(TForm(Owner).Handle, WM_NCACTIVATE, Longint(True), 0);
  end
  else inherited Show;
end;

procedure TCustomAutoDateControl.ShowDate(ADate: TDateTime);
var
  Row: Integer;
begin
  Row := RowOfDate(ADate);
  if Row < TopRow then TopRow := Row;
  if Row >= TopRow + VisibleRowCount then
    TopRow := Row - (VisibleRowCount - 1);
end;

{$IFNDEF AOB_PS}

{ TDropDownButton }

type
  TDropDownButton = class(TSpeedButton)
  private
    IsShowingPopup: Boolean;
    procedure DateChange(Sender: TObject);
    procedure HidePopup(Sender: TObject);
    procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Click; override;
  end;

constructor TDropDownButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Align := alRight;
  Cursor := crArrow;
  Width := GetSystemMetrics(SM_CXVSCROLL);
end;

procedure TDropDownButton.DateChange(Sender: TObject);
begin
  TCustomAutoDateEdit(Parent).Date := TCustomAutoDateControl(Sender).BeginDate;
  if TCustomAutoDateEdit(Parent).SelectPeriod then
    TCustomAutoDateEdit(Parent).EndDate := TCustomAutoDateControl(Sender).EndDate;
end;

procedure TDropDownButton.HidePopup(Sender: TObject);
var
  P: TPoint;
begin
  GetCursorPos(P);
  P := ScreenToClient(P);
  if (GetAsyncKeyState(VK_LBUTTON) shr 15 = 0) or
    not PtInRect(ClientRect, P) then
    IsShowingPopup := False;
end;

procedure TDropDownButton.WMLButtonDown(var Message: TWMLButtonDown);
begin
  if IsShowingPopup then IsShowingPopup := False
  else inherited;
end;

procedure TDropDownButton.Paint;
const
  EnabledStyles: array[Boolean] of Integer = (DFCS_INACTIVE, 0);
  DownStyles: array[Boolean] of Integer = (0, DFCS_PUSHED);
begin
  inherited Paint;
  with Canvas do
    DrawFrameControl(Handle, ClientRect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX or
      EnabledStyles[Parent.Enabled] or DownStyles[FState = bsDown]);
end;

procedure TDropDownButton.Click;
var
  P: TPoint;
  R: TRect;
begin
  if TCustomAutoDateEdit(Parent).ReadOnly then Exit;
  inherited Click;
  Parent.SetFocus;
  with Parent do
    P := Parent.ClientToScreen(Point(Left, Top + Height));
  with TCustomAutoDateControl.Create(GetParentForm(Self)) do
  begin
    IsPopup := True;
    Left := P.X;
    Top := P.Y;
    Height := TCustomAutoDateEdit(Self.Parent).DropDownHeight;
    Font := TCustomAutoDateEdit(Self.Parent).Font;
    SelectPeriod := TCustomAutoDateEdit(Self.Parent).SelectPeriod;
    BeginDate := TCustomAutoDateEdit(Self.Parent).BeginDate;
    EndDate := TCustomAutoDateEdit(Self.Parent).EndDate;
    Date := TCustomAutoDateEdit(Self.Parent).Date;
    OnDateChange := DateChange;
    OnBeginDateChange := DateChange;
    OnEndDateChange := DateChange;
    OnHide := HidePopup;
    SetSize;
    SystemParametersInfo(SPI_GETWORKAREA, 0, @R, 0);
    if Left < R.Left then Left := R.Left;
    if Left + Width > R.Right then
      Left := R.Right - Width;
    if Top + Height > R.Bottom then
      Top := Top - (Self.Parent.Height + Height);
    IsShowingPopup := True;
    Show;
  end;
end;

{ TCustomAutoDateEdit }

constructor TCustomAutoDateEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FDropDownHeight := 200;
  SelectPeriod := False;
  DropDownButton := TDropDownButton.Create(Self);
  DropDownButton.Parent := Self;
  Date := SysUtils.Date;
end;

function TCustomAutoDateEdit.GetBeginDate: TDateTime;
begin
  if BeginDateStr[1] = ' ' then Result := NullDate
  else
    Result := StrToDate(BeginDateStr);
end;

function TCustomAutoDateEdit.GetDate: TDateTime;
begin
  Result := BeginDate;
end;

function TCustomAutoDateEdit.GetEndDate: TDateTime;
begin
  if SelectPeriod then
    Result := StrToDate(EndDateStr)
  else Result := BeginDate;
end;

procedure TCustomAutoDateEdit.SetBeginDate(Value: TDateTime);
var
  AEditText: string;
begin
  if Value <> NullDate then
  begin
    if BeginDate = NullDate then EndDate := Value;
    AEditText := FormatDateTime(DateFormat, Value);
    if SelectPeriod then AEditText := AEditText + '-' + EndDateStr;
    EditText := AEditText;
    if not SelectPeriod then EndDate := Value;
    DoBeginDateChange;
  end;
end;

procedure TCustomAutoDateEdit.SetDate(Value: TDateTime);
begin
  BeginDate := Value;
  DoDateChange;
end;

procedure TCustomAutoDateEdit.SetEndDate(Value: TDateTime);
begin
  if SelectPeriod then
  begin
    EditText := BeginDateStr + '-' + FormatDateTime(DateFormat, Value);
    DoEndDateChange;
  end;
end;

procedure TCustomAutoDateEdit.SetSelectPeriod(Value: Boolean);
var
  IsCreating: Boolean;
  OldBeginDate, OldEndDate: TDateTime;
  OldDefaultBlank: Char;
  NewEditMask: string;
  I: Integer;
begin
  if EditText = '' then IsCreating := True
  else
  begin
    IsCreating := False;
    OldBeginDate := BeginDate;
    OldEndDate := EndDate;
  end;
  FSelectPeriod := Value;
  OldDefaultBlank := DefaultBlank;
  DefaultBlank := ' ';

  if FSelectPeriod then
    NewEditMask := DateFormat + '-' + DateFormat
  else
    NewEditMask := DateFormat;
  for I := 1 to Length(NewEditMask) do
    if UpCase(NewEditMask[I]) in ['D', 'M', 'Y'] then
      NewEditMask[I] := '0';
  MaxLength := Length(NewEditMask);
  EditMask := NewEditMask;

  DefaultBlank := OldDefaultBlank;
  if IsCreating then
  begin
    BeginDate := SysUtils.Date;
    EndDate := SysUtils.Date;
  end
  else
  begin
    BeginDate := OldBeginDate;
    EndDate := OldEndDate;
  end;
end;

procedure TCustomAutoDateEdit.WMEraseBkgnd(var Message: TWMEraseBkgnd);
var
  R: TRect;
begin
  R := ClientRect;
  Dec(R.Right, GetSystemMetrics(SM_CXVSCROLL));
  FillRect(Message.DC, R, Brush.Handle);
  Message.Result := 1;
end;

function TCustomAutoDateEdit.BeginDateStr: string;
begin
  Result := Copy(EditText, 1, Length(DateFormat));
end;

procedure TCustomAutoDateEdit.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do Style := Style or ES_MULTILINE;
end;

function TCustomAutoDateEdit.EditingPlace(var EndDateEditing: Boolean): TEditingPlace;
var
  S: string;
  Pos: Integer;
begin
  S := UpperCase(DateFormat);
  Pos := SelStart + 1;
  if Pos > MaxLength then Pos := MaxLength;
  EndDateEditing := SelectPeriod and (Pos > Length(S) + 1);
  if EndDateEditing then Dec(Pos, Length(S) + 1);
  case S[Pos] of
    'D': Result := epDay;
    'M': Result := epMonth;
    'Y': Result := epYear;
  else Result := epError;
  end;
end;

function TCustomAutoDateEdit.EndDateStr: string;
begin
  Result := Copy(EditText, Length(DateFormat) + 2, Length(DateFormat));
end;

procedure TCustomAutoDateEdit.KeyDown(var Key: Word; Shift: TShiftState);

  procedure ChangeValue(Delta: Integer);
  var
    OldSelStart, OldSelStop: Integer;
    AEditingPlace: TEditingPlace;
    EndDateEditing: Boolean;
    ADate: TDateTime;
    Year, Month, Day: Word;
  begin
    GetSel(OldSelStart, OldSelStop);
    AEditingPlace := EditingPlace(EndDateEditing);
    if EndDateEditing then ADate := EndDate
    else ADate := BeginDate;
    if AEditingPlace = epDay then
      ADate := ADate + Delta
    else
    begin
      DecodeDate(ADate, Year, Month, Day);
      if AEditingPlace = epMonth then
        if Delta = -1 then DecMonth(Year, Month)
        else IncMonth(Year, Month)
      else
        if AEditingPlace = epYear then Inc(Year, Delta)
        else Exit;
      if Day > DaysPerMonth(Year, Month) then
        Day := DaysPerMonth(Year, Month);
      ADate := EncodeDate(Year, Month, Day);
    end;
    if EndDateEditing then EndDate := ADate
    else BeginDate := ADate;
    SetSel(OldSelStart, OldSelStop);
  end;

begin
  if not ReadOnly then
    case Key of
      VK_DOWN:
        if Shift = [ssAlt] then DropDownButton.Click
        else ChangeValue(-1);
      VK_UP: ChangeValue(1);
      VK_F4: if Shift = [] then DropDownButton.Click;
    end;
  inherited KeyDown(Key, Shift);
end;

procedure TCustomAutoDateEdit.DoDateChange;
begin
  if Assigned(FOnDateChange) then FOnDateChange(Self);
end;

procedure TCustomAutoDateEdit.DoBeginDateChange;
begin
  if Assigned(FOnBeginDateChange) then FOnBeginDateChange(Self);
  DoDateChange;
end;

procedure TCustomAutoDateEdit.DoEndDateChange;
begin
  if Assigned(FOnEndDateChange) then FOnEndDateChange(Self);
end;

procedure TCustomAutoDateEdit.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
  R: TRect;
  W: Integer;
begin
  inherited SetBounds(ALeft, ATop, AWidth, AHeight);
  if HandleAllocated then
  begin
    Perform(EM_GETRECT, 0, Longint(@R));
    W := GetSystemMetrics(SM_CXVSCROLL);
    if BorderStyle = bsSingle then Dec(W);
    if (R.Right - R.Left <= ClientWidth - W) or
      (Width = 0) or (Height = 0) then Exit;
    Dec(R.Right, W);
    Perform(EM_SETRECT, 0, Longint(@R));
    DropDownButton.Height := ClientHeight;
  end;
end;

{$ENDIF}

var
  P, I: Integer;

initialization
  ScrollTimerID := -1;
  DateFormat := ShortDateFormat;
  // extends to min 2 char places for editing month and day
  P := 1;
  for I := 1 to Length(DateFormat) do
    if (DateFormat[I] = DateSeparator) and (I - P = 1) then
    begin
      Insert(DateFormat[I - 1], DateFormat, I);
      P := I + 2;
    end;
  // extends to 4 digit year editing field
//  if UpperCase(Copy(DateFormat, Length(DateFormat) - 3, 4)) <> 'YYYY' then
//    DateFormat := DateFormat + 'yy';

end.

⌨️ 快捷键说明

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