在我的应用程序中,我有一个主窗体,能够在数据库中加载一些图像。 在加载图片时,我想显示一个带有进度指示器的表单(带有bsNone边框样式)。
但是,如果我使用ShowModal显示表单,则停止执行主表单,所以我不能这样做。
如果我调用Show,用户可以访问所有其他表单组件,这可能很危险,而照片未完全加载。
我需要弄清楚方法,禁用主窗体上的所有内容,而加载没有完成。
请告诉我,这是怎么可能的。
答案 0 :(得分:12)
将MainForm
设置为进度表单的PopupParent
,以便MainForm
永远不会出现在进度表单的顶部。然后只需在进度表单打开时设置MainForm.Enabled := False
,并在进度表单关闭时设置MainForm.Enabled := True
。
procedure TMainForm.ShowProgressForm;
begin
with TProgressForm.Create(nil) do
begin
PopupParent := Self;
OnClose := ProgressFormClose;
Show;
end;
Enabled := False;
end;
procedure TMainForm.ProgressFormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
Enabled := True;
end;
这模拟了ShowModal()
对用户的行为(MainForm
在进度表打开时不是用户交互的),但没有阻止代码。
答案 1 :(得分:4)
首先,直接回答你的问题。
我需要弄明白,禁用主表单上的所有内容。
将MainForm.Enabled
设置为False
以禁用与主窗体关联的窗口。要重新启用,请将其设置为True
。
然而,您的基本问题是您正在GUI线程中执行长时间运行的任务。这总是一个坏主意,出路就是在一个单独的线程中执行那些长时间运行的任务。
将长时间运行的任务移至单独的主题后,您会发现ShowModal
正是您展示进度表所需的。
答案 2 :(得分:1)
正如我在其他答案中解释的那样,将长时间运行的任务放入GUI线程以外的线程中是理想的解决方案。 GUI线程应该处理短期运行的任务,以便它始终能够及时地为消息队列提供服务。
但是,如果您已经拥有假定长时间运行的任务在GUI线程上运行的代码,您可能更愿意采用更方便的方法并将重新分解推迟到线程代码。在这种情况下,在我看来,最好使用ShowModal
来显示您的进度表单。
但是为了使这项工作成功,你需要找到一个让长时间运行的任务在ShowModal
调用中执行。你可以这样做:
ShowModal
之前,将任务传递给表单。例如,将TProc
传递给进度表单的构造函数。Activate
方法。这将在ShowModal
函数启动其模态消息循环之前执行。在Activate
的实现中,向表单发布消息。显然,您需要在长时间运行的任务中调用ProcessMessages
,以便保持主GUI线程消息队列的服务。显然你必须已经这样做了。
答案 3 :(得分:1)
您可能希望使用DisableTaskWindows
和EnableTaskWindows
功能。
如果您对父表单使用简单ParentForm.Enabled := False
,您仍然可以访问所有其他表单,例如主表单与ParentForm
不同。这显然仍然很危险。
这是简短的样本:
interface
uses
Vcl.Forms, Winapi.Windows, ...;
type
TPleaseWait = class(TObject)
private
fWindowList: TTaskWindowList;
fActiveWindow: HWND;
fDialog: TPleaseWaitForm;
public
constructor Create;
destructor Destroy; override;
end;
implementation
constructor TPleaseWait.Create;
begin
// Disable all displayed windows
fWindowList := DisableTaskWindows(0);
// Save the last active window
fActiveWindow := GetActiveWindow;
fDialog := TPleaseWaitForm.Create(nil);
fDialog.Show;
end;
destructor TPleaseWait.Destroy;
const
INVALID_HANDLE = 0;
begin
fDialog.Close;
fDialog.Free;
// All windows are enabled now
EnableTaskWindows(fWindowList);
// That helps by enabling the last one form
if (fActiveWindow <> INVALID_HANDLE) and IsWindow(fActiveWindow) then
SetActiveWindow(fActiveWindow);
inherited;
end;
end.
答案 4 :(得分:1)
设置子窗体的PopupParent = ParentForm
procedure TParentForm.Button1Click(Sender: TObject);
begin
ParentForm.Enabled:=False;
with Tform1.create(nil) do show;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ParentForm.Enabled := true;
form1.free;
end;