我希望我的表格在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;)。你如何在代码中检测到合适的时间?
答案 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
....