Delphi在加载时禁用表单

时间:2013-07-06 16:08:04

标签: delphi tform

在我的应用程序中,我有一个主窗体,能够在数据库中加载一些图像。 在加载图片时,我想显示一个带有进度指示器的表单(带有bsNone边框样式)。

但是,如果我使用ShowModal显示表单,则停止执行主表单,所以我不能这样做。

如果我调用Show,用户可以访问所有其他表单组件,这可能很危险,而照片未完全加载。

我需要弄清楚方法,禁用主窗体上的所有内容,而加载没有完成。

请告诉我,这是怎么可能的。

5 个答案:

答案 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调用中执行。你可以这样做:

  1. 在致电ShowModal之前,将任务传递给表单。例如,将TProc传递给进度表单的构造函数。
  2. 覆盖进度表单的Activate方法。这将在ShowModal函数启动其模态消息循环之前执行。在Activate的实现中,向表单发布消息。
  3. 当表单处理该消息时,调用传递给构造函数的任务。
  4. 显然,您需要在长时间运行的任务中调用ProcessMessages,以便保持主GUI线程消息队列的服务。显然你必须已经这样做了。

答案 3 :(得分:1)

您可能希望使用DisableTaskWindowsEnableTaskWindows功能。

如果您对父表单使用简单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;