6

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

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

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

我需要设法禁用主窗体上的所有内容,而加载尚未完成。

请告诉我,这怎么可能。

4

5 回答 5

16

将 设置MainFormPopupParent进度表单,以便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进度表单打开时不是用户交互的),但不会阻塞代码。

于 2013-07-06T16:27:48.653 回答
6

首先,直接回答你的问题。

我需要设法禁用主窗体上的所有内容。

设置MainForm.EnabledFalse禁用与主窗体关联的窗口。并重新启用将其设置为True


但是,您的基本问题是您正在 GUI 线程中执行长时间运行的任务。这总是一个坏主意,出路是在单独的线程中执行那些长时间运行的任务。

将长时间运行的任务移至单独的线程后,您会发现这ShowModal正是您显示进度表所需要的。

于 2013-07-06T16:12:00.557 回答
2

正如我在另一个答案中解释的那样,将长时间运行的任务放入 GUI 线程以外的线程是理想的解决方案。GUI 线程应该处理短时间运行的任务,以便它始终能够及时地为消息队列提供服务。

但是,如果您已经有假设长时间运行的任务在 GUI 线程上运行的代码,您可能更愿意采用更方便的方法并将重构推迟到线程代码。在这种情况下,在我看来,最好还是用它ShowModal来显示你的进度表。

但是为了使这项工作正常进行,您需要在ShowModal调用中找到一个让长时间运行的任务执行。你可以这样做:

  1. 在调用之前ShowModal,将任务传递给表单。例如,将 a 传递TProc给进度表单的构造函数。
  2. 覆盖进度表的Activate方法。ShowModal这将在函数开始其模式消息循环之前执行。在执行中Activate,向表单发布消息。
  3. 当表单处理该消息时,调用传递给构造函数的任务。

显然,您需要调用ProcessMessages长时间运行的任务,以保持主 GUI 线程消息队列得到服务。显然,您必须已经这样做了。

于 2013-07-07T21:01:27.723 回答
1

您可能想要使用 DisableTaskWindowsEnableTaskWindows功能。

如果您ParentForm.Enabled := False对父表单使用 simple,您仍然可以访问所有其他表单,例如主表单(如果它不同于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.
于 2015-11-04T15:17:53.803 回答
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;
于 2017-09-29T23:06:42.883 回答