我有一个需要花费几秒钟来加载的应用程序(大量的初始化)。 GUI在启动期间冻结。因此,我想创建一个初始屏幕,该初始屏幕在应用程序加载时淡入和淡出。我使用TBackgroundWorker组件在后台线程中制作动画。
但是,当我使用此组件时,会发生一些奇怪的事情:当它表示“工作已完成”(请参阅BackgroundWorkerWorkComplete)时,我同时打开的消息对话框将自动关闭。
procedure TMainForm.ButtonStartSplashClick(Sender: TObject);
VAR
frmSplash: TfrmSplash;
begin
frmSplash:= TfrmSplash.Create(NIL);
frmSplash.StartAnimation;
//MessageBox(Handle, 'Hi', nil, MB_OK); // This remains on screen
Application.MessageBox(PChar('Hi'), PChar('Box'), MB_ICONINFORMATION); // This is automatically closed when the background thread is done
end;
这是启动画面:
procedure TfrmSplash.StartAnimation;
begin
Show;
BackgroundWorker.Execute;
end;
procedure TfrmSplash.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= caFree;
end;
procedure TfrmSplash.BackgroundWorkerWork(Worker: TBackgroundWorker);
VAR i: Integer;
begin
for i:= 1 to 255 DO
begin
AlphaBlendValue:= i; // do not access GUI directly from thread
Sleep(30);
end;
end;
procedure TfrmSplash.BackgroundWorkerWorkComplete(Worker: TBackgroundWorker; Cancelled: Boolean);
begin
Close; // At this point, the msg box will be closed also
end;
我发现奇怪的是,MessageBox仍保留在屏幕上,而Application.MessageBox没有(自动关闭)。
为什么关闭TfrmSplash也将关闭消息框?
TApplication.MessageBox是WinAPI MessageBox函数的包装。前者的代码向您展示了它的调用方式:
function TApplication.MessageBox(const Text, Caption: PChar; Flags: Longint): Integer;
var
ActiveWindow, TaskActiveWindow: HWnd;
MBMonitor, AppMonitor: HMonitor;
MonInfo: TMonitorInfo;
Rect: TRect;
FocusState: TFocusState;
WindowList: TTaskWindowList;
begin
ActiveWindow := ActiveFormHandle;
if ActiveWindow = 0 then
TaskActiveWindow := Handle
else
TaskActiveWindow := ActiveWindow;
{ ... }
try
Result := Winapi.Windows.MessageBox(TaskActiveWindow, Text, Caption, Flags);
finally
注意,传递给WinAPI调用的HWND是TaskActiveWindow
,它在进行调用时被视为活动窗口(除非没有,在这种情况下,将使用应用程序的句柄)。由于您刚刚创建了TFrmSplash,它将成为活动窗口,并且在关闭其父窗口(您的启动窗口)时将丢弃消息框。
当您直接直接调用MessageBox时:
MessageBox(Handle, 'Hi', nil, MB_OK); // This remains on screen
您正在传递Handle
,这是您从中调用代码的表单的句柄,在本例中为TMainForm
,因此在这种情况下,主表单成为所有者,并且与启动画面。