在一天中的某些时段关闭表格

时间:2014-09-11 23:07:45

标签: delphi

我希望我的表格在06:00,12:00和24:00结束时关闭。 但在关闭之前,我想显示一个进度条 表单关闭前剩余多少时间(进度时) 栏达到100% - 表格关闭)。我怎么能这样做?

编辑: 我这样做了:

procedure TMainForm.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
 AdvOfficeStatusBar1.Panels[4].Progress.Position := AdvOfficeStatusBar1.Panels[4].Progress.Position +1;
 if AdvOfficeStatusBar1.Panels[4].Progress.Position = 100 then begin
 MainForm.Close;
 end;
 Timer1.Enabled := True;
end;

所以我需要的是另一个计时器,它可以检测当天的时间并在指定的时间点击进度条(Timer1.Enabled:= True;)。你如何在代码中检测到合适的时间?

3 个答案:

答案 0 :(得分:5)

我建议您调查等待计时器。这些可以设置为在特定时间段(如常规TTimer)之后或在一天中的指定时间触发,这正是您在这种情况下所需的。

在您的表单创建/显示活动中,创建一个可等待的计时器,并将其设置为您希望它发布的所需时间。 (它只是你的候选关闭时间之一,即在当前时间之后发生的下一个关闭时间)。在你的情况下,我相信你提到倒计时在关闭时间前90秒开始,所以这是你的"到期时间"等待计时器(下一个T - 90秒)。

您设置的截止时间必须在 FILETIME 中指定,并且必须是UTC,而不是本地时间。这很繁琐,但并不是特别困难。

计算下一个自动关闭时间,减去90秒。然后使用 DateTimeToSystemTime(localTDateTime,localSYSTEMTIME) SYSTEMTIME 表示中生成 TDateTime 值,然后您可以将其传递给TzSpecificLocalTimeToSystemTime()转换为UTC SYSTEMTIME

然后,您只需将UTC SYSTEMTIME 转换为 FILETIME SysUtils 中的 SystemTimeToFileTime())。

回调proc是一个第一类proc,而不是表单方法,并且必须符合预期的回调签名。

回调proc将在一个单独的线程中调用,因此启动倒计时器的回调实现必须是线程安全的。实现这一目标的最简单方法是利用消息队列并简单地将消息发送(或发布)到表单,然后通过启动倒数计时器来响应。为了确保使用正确的窗口句柄,可以将其传递给回调过程。由于HWND适合指针,因此可以通过类型转换直接传递指针中的HWND。

你的回调过程将如下所示:

procedure TimerCallbackProc(aData: Pointer; aTimerLo, aTimerHi: DWORD);
begin
  PostMessage(HWND(aData), MM_STARTCOUNTDOWNTIMER, 0, 0);
end;

MM_STARTCOUNTDOWNTIMER 是一个基于 WM_USER 的私有消息,表单处理该消息以启动倒数计时器:

注意:您的表单必须在关闭时取消回拨计时器,或者在计时器被解除之前取消#t;或者因此而来。

将所有这些放在一起,你应该得到类似的东西:

const
  MM_STARTCOUNTDOWNTIMER = WM_USER + 1;


type
  TMyForm = class(TForm)
    fCloseCountdownTimer: TTimer;
    fCloseTimer: HANDLE;
    ..
    procedure MMStartCountdownTimer(var aMessage: TMessage); message MM_STARTCOUNTDOWNTIMER;
  end;


  procedure TMyForm.FormCreate(Sender: TObject); 
  begin
     ..

     ..
     fCloseTimer := CreateWaitableTimer( .. );
     SetWaitableTimer( fCloseTimer, dueTime, 0, TimerCallbackproc, Pointer(Handle), TRUE );
  end;


  procedure TMyForm.FormClose(Sender: TObject); 
  begin
    CancelWaitableTimer( fCloseTimer );
  end;


  procedure TMyForm.MMStartCountdownTimer(var aMessage: TMessage); 
  begin
    fCloseCountdownTimer.Enabled := TRUE;
  end;

注意:上面代码中对 SetWaitableTimer()的调用中的最终 TRUE 参数可确保如果系统在计时器触发时暂停,则系统将唤醒以处理计时器。如果这不是您想要的,那么只需传递FALSE,并且计时器不会唤醒睡眠系统(但是如果在系统处于睡眠状态时已经过去了,您的表格将不会自动关闭。)

有关更多更具体的详细信息,建议您参考Waitable Timer API documentation from Microsoft

答案 1 :(得分:1)

您只需要一个计时器。此计时器将当前时间与目标时间进行比较,确定每次之间的秒数。如果它在一定范围内,则显示并设置进度条。否则,它会隐藏它。

以下是一个演示应用程序。保存这些文件并将它们添加到项目中。当你开始它时,等待30秒让它开始倒计时(时间设置为2分钟,倒计时持续90秒)。

<强> Unit1.dfm

object Form1: TForm1
  Left = 310
  Top = 121
  Caption = 'Form1'
  ClientHeight = 188
  ClientWidth = 562
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 41
    Top = 28
    Width = 44
    Height = 13
    Caption = 'Close At:'
  end
  object Label2: TLabel
    Left = 216
    Top = 28
    Width = 44
    Height = 13
    Caption = 'Warning:'
  end
  object lblSecondsLeft: TLabel
    Left = 41
    Top = 143
    Width = 62
    Height = 13
    Caption = 'Seconds Left'
    Visible = False
  end
  object ProgressBar1: TProgressBar
    Left = 41
    Top = 120
    Width = 448
    Height = 17
    TabOrder = 0
    Visible = False
  end
  object dtTime: TDateTimePicker
    Left = 96
    Top = 24
    Width = 97
    Height = 21
    Date = 41893.905071574070000000
    Time = 41893.905071574070000000
    Kind = dtkTime
    TabOrder = 1
  end
  object BitBtn1: TBitBtn
    Left = 392
    Top = 25
    Width = 97
    Height = 21
    Caption = 'Save'
    TabOrder = 2
    OnClick = BitBtn1Click
  end
  object txtWarning: TEdit
    Left = 272
    Top = 25
    Width = 97
    Height = 21
    TabOrder = 3
    Text = '90'
  end
  object Timer1: TTimer
    OnTimer = Timer1Timer
    Left = 360
    Top = 72
  end
end

<强> Unit1.pas

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ComCtrls,
  Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;
    dtTime: TDateTimePicker;
    BitBtn1: TBitBtn;
    Timer1: TTimer;
    Label1: TLabel;
    Label2: TLabel;
    txtWarning: TEdit;
    lblSecondsLeft: TLabel;
    procedure BitBtn1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private

  public
    CloseTime: TDateTime;
    SecondsToClose: Integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  DateUtils;

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  Timer1.Enabled:= False;
  CloseTime:= dtTime.Time;
  SecondsToClose:= StrToIntDef(txtWarning.Text, 90);
  txtWarning.Text:= IntToStr(SecondsToClose); //sanity check
  ProgressBar1.Max:= SecondsToClose;
  Timer1.Enabled:= True;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  dtTime.DateTime:= DateUtils.IncMinute(Now, 2);
  BitBtn1Click(nil);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  T: TDateTime;
  Secs: Integer;
begin
  T:= Now;
  Secs:= SecondsBetween(T, CloseTime);
  if T >= CloseTime then begin
    if Secs < 30 then //Only if within 30 seconds of close time
      Close;
  end else
  if (Secs <= SecondsToClose)  then begin
    ProgressBar1.Visible:= True;
    lblSecondsLeft.Visible:= True;
    ProgressBar1.Position:= ProgressBar1.Max - Secs;
    lblSecondsLeft.Caption:= IntToStr(Secs)+' Seconds Before Close';
  end else begin
    ProgressBar1.Visible:= False;
    lblSecondsLeft.Visible:= False;
  end;
end;

end.

答案 2 :(得分:0)

我认为一种巧妙的方法是将所需的时间放在数据库中并让表格检查时间:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
if  FDTable1.Locate('Time',StrToTime(FormatDateTime('hh:mm:ss',now))]),[])   then
begin
....