在过去,一个简单的 PostMessage 就可以解决问题。本质上,您在基本表单的 DoShow 期间触发它:
procedure TBaseForm.DoShow;
begin
inherited;
PostMessage(Handle, APP_AFTERSHOW, 0, 0);
end;
然后捕获消息并为从该基本表单继承的所有表单创建一个 AfterShow 事件。
但这不再有效,如果您正在剥皮并拥有大量 VCL 控件,那就不行了。
我的下一个技巧是在 DoShow 中生成一个简单线程并检查 IsWindowVisible(Handle) 和 IsWindowEnabled(Handle)。这确实加快了速度,因为数据库打开和其他东西已经在 AfterShow 事件中,所以它从加载时间减少了 250 毫秒。
最后我想到了 madHooks,它很容易为我的应用程序挂钩 API ShowWindow 并从中触发 APP_AFTERSHOW。
function ShowWindowCB(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall;
begin
Result := ShowWindowNext(hWnd, nCmdShow);
PostMessage(hWnd, APP_AFTERSHOW, 0, 0);
end;
procedure TBaseForm.Loaded;
begin
inherited;
if not Assigned(Application.MainForm) then // Must be Mainform it gets assigned after creation completes
HookAPI(user32, 'ShowWindow', @ShowWindowCB, @ShowWindowNext);
end;
为了让整个事情在 AfterShow 之前完全绘制,它仍然需要一个 ProcessPaintMessages 调用
procedure TBaseForm.APPAFTERSHOW(var AMessage: TMessage);
begin
ProcessPaintMessages;
AfterShow;
end;
procedure ProcessPaintMessages; // << not tested, pulled out of code
var
msg: TMsg;
begin
while PeekMessage(msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) do
DispatchMessage(msg);
end;
我的最后一个测试是在 AfterShow 事件中添加一个 Sleep,并看到由于 AfterShow 事件尚未完成而完全用空 db 容器绘制的表单。
procedure TMainForm.AfterShow;
begin
inherited;
Sleep(8*1000);
......